Spring
2021-10-14 14:36:57 18 举报
AI智能生成
spring
作者其他创作
大纲/内容
AOP
面向切面编程思想:
简单的说它就是把我们程序重复的代码抽取出来,在需要执行的时候,
使用动态代理的技术,在不修改源码的基础上,对我们的已有方法进行增强。
作用:
在程序运行期间,不修改源码对已有方法进行增强。
优势:
减少重复代码提高开发效率
维护方便
简单的说它就是把我们程序重复的代码抽取出来,在需要执行的时候,
使用动态代理的技术,在不修改源码的基础上,对我们的已有方法进行增强。
作用:
在程序运行期间,不修改源码对已有方法进行增强。
优势:
减少重复代码提高开发效率
维护方便
底层为动态代理:对目标类进行功能增强
AOP相关术语
Joinpoint(连接点)
所谓连接点是指那些被拦截到的点。在spring中,这些点指的是方法,因为spring只支持方法类型的连接点
Pointcut(切入点)
所谓切入点是指我们要对哪些Joinpoint进行拦截的定义。
理解:就是需要增强的方法 不是所有方法都需要增强
理解:就是需要增强的方法 不是所有方法都需要增强
Advice(通知/增强)
所谓通知是指拦截到 Joinpoint 之后所要做的事情就是通知。
通知的类型:前置通知,后置通知,异常通知,最终通知,环绕通知。
通知的类型:前置通知,后置通知,异常通知,最终通知,环绕通知。
Introduction(引介)
引介是一种特殊的通知在不修改类代码的前提下,
Introduction可以在运行期为类动态地添加一些方法或Field。
Introduction可以在运行期为类动态地添加一些方法或Field。
Target(目标对象)
代理的目标对象
Weaving(织入)
是指把增强应用到目标对象来创建新的代理对象的过程。
spring 采用动态代理织入,而 AspectJ 采用编译期织入和类装载期织入。
spring 采用动态代理织入,而 AspectJ 采用编译期织入和类装载期织入。
Proxy(代理)
一个类被AOP织入增强后,就产生一个结果代理类
Aspect(切面)
是切入点和通知(引介)的结合
对目标类方法进行加强的方式
继承
缺点:需要直到要增强的方法的类才能继承
装饰者模式
装饰者模式就是静态代理的一种体现。
缺点:静态代理是字节码一上来就创建好,并完成加载,代码是写死的。
需要有接口,这个接口下除了要增强的方法外别的方法也要实现
需要有接口,这个接口下除了要增强的方法外别的方法也要实现
动态代理模式
特点
字节码随用随创建,随用随加载
基于接口的动态代理:JDK官方的Proxy类提供
要求:需要接口但可以指定增强方法,不需要实现全部方法
步骤
创建代理对象
Proxy.newProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHandler h)
参数含义:
ClassLoader:和被代理对象使用相同的类加载器。
Interfaces:和被代理对象具有相同的行为。实现相同的接口。
InvocationHandler:该接口的实现类(一般写匿名内部类)如何代理(如何增强)。
策略模式:使用场景是:
数据有了,目的明确。
如何达成目标,就是策略。
ClassLoader:和被代理对象使用相同的类加载器。
Interfaces:和被代理对象具有相同的行为。实现相同的接口。
InvocationHandler:该接口的实现类(一般写匿名内部类)如何代理(如何增强)。
策略模式:使用场景是:
数据有了,目的明确。
如何达成目标,就是策略。
如何代理
重写的invoke方法
Object invoke(Object proxy, Method method, Object[] args) throws Throwable
执行被代理对象的任何方法,都会经过该方法。此方法有拦截的功能。
参数:
proxy:代理对象的引用。不一定每次都用得到
method:当前执行的方法对象
args:执行方法所需的参数
返回值:
当前执行方法的返回值
谁调用返回给谁 返回的内容就是最终值
而不是需要增强的方法的返回值
proxy:代理对象的引用。不一定每次都用得到
method:当前执行的方法对象
args:执行方法所需的参数
返回值:
当前执行方法的返回值
谁调用返回给谁 返回的内容就是最终值
而不是需要增强的方法的返回值
代码示例
final Producer producer=new Producer();
IProducer proxy_producer=(IProducer) Proxy.newProxyInstance(producer.getClass().getClassLoader(),
producer.getClass().getInterfaces(), new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Float money=(Float)args[0];
if(method!=null&&method.getName().equals("saleProduct")){
return method.invoke(producer,money*0.8f);//对参数列表进行增强
}
return method.invoke(producer,args);//未增强
}
});
proxy_producer.afterService(1000f);
IProducer proxy_producer=(IProducer) Proxy.newProxyInstance(producer.getClass().getClassLoader(),
producer.getClass().getInterfaces(), new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Float money=(Float)args[0];
if(method!=null&&method.getName().equals("saleProduct")){
return method.invoke(producer,money*0.8f);//对参数列表进行增强
}
return method.invoke(producer,args);//未增强
}
});
proxy_producer.afterService(1000f);
细节
内部类中使用但未声明的任何局部变量必须在内部类的正文之前明确分配。
Java匿名内部类的方法中用到的局部变量都必须定义为final,原因:
java中规定,内部类只能访问外部类中的成员变量,不能访问方法中定义的变量,
变量声明为final(常量)的,使变量全局化,
就相当于是在外部定义的而不是在方法里定义的
Java匿名内部类的方法中用到的局部变量都必须定义为final,原因:
java中规定,内部类只能访问外部类中的成员变量,不能访问方法中定义的变量,
变量声明为final(常量)的,使变量全局化,
就相当于是在外部定义的而不是在方法里定义的
通过if-else判断方法名来增强指定方法
基于子类的动态代理:第三方的CGLib提供
要求:被代理对象不能是最终类
不需要接口,可以指定增强方法,但代码量比较大
CGLIB原理
该代理模式是用拦截器和过滤器的方式进行继承,对目标类的方法进行增强
步骤
创建代理对象
Enhancer.create(Class, Callback)
方法的参数:
Class:被代理对象的字节码
Callback:如何代理
Class:被代理对象的字节码
Callback:如何代理
如何代理,重写intercept方法,类似上面
参数:前三个和基于接口的动态代理是一样的。
MethodProxy:当前执行方法的代理对象。
MethodProxy:当前执行方法的代理对象。
代码示例
//基于子类的动态代理
IProducer cglib_producer= (IProducer) Enhancer.create(producer.getClass(), new MethodInterceptor() {
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
Float money=(Float)objects[0];
if(method!=null&&method.getName().equals("afterService")){
return method.invoke(producer,money*0.8f);//对参数列表进行增强
}
return method.invoke(producer,objects);//未增强
}
});
cglib_producer.afterService(1000f);
IProducer cglib_producer= (IProducer) Enhancer.create(producer.getClass(), new MethodInterceptor() {
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
Float money=(Float)objects[0];
if(method!=null&&method.getName().equals("afterService")){
return method.invoke(producer,money*0.8f);//对参数列表进行增强
}
return method.invoke(producer,objects);//未增强
}
});
cglib_producer.afterService(1000f);
AOP的三种方式
基于XML配置
步骤
确定目标类(要被切的类,即需要被加强的类),需要定义切入点,此处切入点为需要增强的方法
确定切面类,即用来切类的刀,需要定义增强后的方法
织入配置,把增强方法指定在切入点前置、后置、环绕、异常、最终 执行
切入点表达式写法
表达式语法:execution([修饰符] 返回值类型 包名.类名.方法名(参数))
全通配方式: * *..*.*(..)
通常情况下,我们都是对业务层的方法进行增强,所以切入点表达式都是切到业务层实现类。
execution(* com.google.service.impl.*.*(..))
execution(* com.google.service.impl.*.*(..))
配置文件
环绕通知配置
<aop:config>
<aop:aspect id="logAdvice" ref="logger">
<!--配置环绕通知 详细注释在Logger实体类中-->
<aop:around method="aroundPrintLog" pointcut-ref="accountCut"></aop:around>
</aop:aspect>
</aop:config>
<aop:aspect id="logAdvice" ref="logger">
<!--配置环绕通知 详细注释在Logger实体类中-->
<aop:around method="aroundPrintLog" pointcut-ref="accountCut"></aop:around>
</aop:aspect>
</aop:config>
它是spring框架为我们提供的一种可以在代码中手动控制增强代码什么时候执行的方式
通常情况下,环绕通知都是独立使用的
通常情况下,环绕通知都是独立使用的
Logger类中环绕通知方法代码
Object[ ] args=pjp.getArgs;//参数为切入点的参数
Obeject rtvalur=proceed(args)//返回值为切入点的返回值
Obeject rtvalur=proceed(args)//返回值为切入点的返回值
织入时机
前置增强
用<aop:before>标签配置
切入点即需要增强的方法,在切入点前介入即在需要增强的方法前介入其他业务逻辑,例如在执行
原方法前打印日志或者其他操作
原方法前打印日志或者其他操作
后置增强
用<aop:after-returning>标签配置
在原方法执行结束后介入增强后的方法
环绕增强
用<aop:around>标签配置,需要在增强后的方法中传参:ProceedingJoinPoint类型的对象,这个对象
中包含着目标类需要增强的方法,需要在增强后的方法中调用pjp.proceed()方法,相当于调用原方法,
在该方法前后需要写自己的业务逻辑实现环绕介入
中包含着目标类需要增强的方法,需要在增强后的方法中调用pjp.proceed()方法,相当于调用原方法,
在该方法前后需要写自己的业务逻辑实现环绕介入
异常增强
用<aop:after-throwing>标签配置
在原方法发生异常时会介入,一般用于对数据库事务的处理
最终增强
用<aop:after>标签配置
在需要增强的方法正确地返回之后执行
基于接口规范式
步骤
定义切面类,让其实现特定接口(五类织入时机)
确定目标类(被介入的类),确定其切点
确定切面类(做介入角色的类)
配置文件
前置增强
定义的切面类需要实现MethodBeforeAdvice接口并实现befor方法
该方式底层会调用目标类中需要增强的方法,在该切面类中的before方法中只需要写自己的业务逻辑即可
后置增强
定义的切面类需要实现AfterReturnAdvice接口并实现afterReturn方法
参数
Object returnValue
原方法返回值
Method method
目标方法
Object[] args
目标方法执行需要的参数数组
Object target
目标对象,即被代理的类的对象
环绕增强
切面类需要实现MethodInterceptor接口并实现invoke(MethodInvocation invocation)方法
这种增强方式需要在invoke方法中调用invocation的proceed()方法调用目标方法,在该语句前后实现自己的
业务逻辑达到环绕增强的效果
业务逻辑达到环绕增强的效果
异常增强
切面类实现ThrowsAdvice接口,该接口是一个空接口,需要在该切面类中自定义名为afterThrowing(Exception e)方法
最终增强
基于注解配置
基本不用配置文件
基于注解整合时,导入约束时需要多导入一个context名称空间下的约束。
xml开启注解
<!--配置spring创建容器时要扫描的包-->
<context:component-scan base-package="com.google"></context:component-scan>
<!--配置spring开启注解AOP的支持-->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
<context:component-scan base-package="com.google"></context:component-scan>
<!--配置spring开启注解AOP的支持-->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
用IOC实例化对象
注解
@Aspect
@Component("txManager")
@Aspect//表明当前类是一个切面类
public class TransactionManager {...}
@Aspect//表明当前类是一个切面类
public class TransactionManager {...}
@Pointcut
@Pointcut("execution(* com.google.service.impl.*.*(..))")
private void accountCut(){}
private void accountCut(){}
@Before
@AfterReturning
@AfterThrowing
@After
@Around
单独使用
单独使用
纯注解:
@Configuration
@ComponentScan(basePackages="com.google")
@EnableAspectJAutoProxy //多了一行 spring开启注解AOP的支持
public class SpringConfiguration { }
@Configuration
@ComponentScan(basePackages="com.google")
@EnableAspectJAutoProxy //多了一行 spring开启注解AOP的支持
public class SpringConfiguration { }
spring新注解(配置我们无法修改的类和配置扫描注解的类路径)
与IOC一样
与IOC一样
写一个类,用新注解标注,可以让该类的作用和application.xml的作用一样
@Configuration
细节:表名该被标注的类是一个配置类,但本质作用并不是标注它是一个配置类,而是加上这个注解之后,该类中的所有方法会被
CGLib代理,方法会跟原来的方法完全不一样,这样就保证了对象的生命周期和作用域。
如果不加该注解,在该类中用Bean注解照样好用,用AnnotationApplicationContext去容器取对象也好用,只不过如果该类
中的一个创建对象的方法调用了另一个创建对象的方法,那么另一个对象将被创建多次,不能保证对象的单例,即作用域scope
不能被保证
CGLib代理,方法会跟原来的方法完全不一样,这样就保证了对象的生命周期和作用域。
如果不加该注解,在该类中用Bean注解照样好用,用AnnotationApplicationContext去容器取对象也好用,只不过如果该类
中的一个创建对象的方法调用了另一个创建对象的方法,那么另一个对象将被创建多次,不能保证对象的单例,即作用域scope
不能被保证
@ComponentScan
通过该注解告诉spring在创建容器时需要扫描的包
属性value,和xml配置文件中的<context:component-scan>标签中的basePackage属性作用一样
@Bean
用于标注配置类中的方法,将方法的返回值对象存入spring容器中
name属性用于定义bean的id,当不写时,存入容器集合时key默认为方法名,值为对象
依然可以使用@Scope:定义类的单例多例
@PropertySource
用于加载.properties文件中的配置。例如我们配置数据源时,
可以把连接数据库的信息写到properties配置文件中,
就可以使用此注解指定properties配置文件的位置
可以把连接数据库的信息写到properties配置文件中,
就可以使用此注解指定properties配置文件的位置
属性: value[]:用于指定properties文件位置。
如果是在类路径下,需要写上classpath:
如果是在类路径下,需要写上classpath:
@Import
用于导入其他配置类,在引入其他配置类时,可以不用再写@Configuration注解。
细节:当我们使用注解配置方法时,如果方法有参数,spring会自动去容器中找有没有可用的bean,查找的方式与@Autowired
注解的方式一致
注解的方式一致
我们已经把要配置的都配置好了,但是新的问题产生了,由于没有配置文件了,如何获取容器呢?
此时不再使用ClassPathXmlApplicationContext,而是要用AnnotationConfigApplication,并将该配置类的class对象传入
此时不再使用ClassPathXmlApplicationContext,而是要用AnnotationConfigApplication,并将该配置类的class对象传入
Spring事务管理
Spring的事务控制都是基于AOP的,它既可以使用编程的方式实现,
也可以使用配置的方式实现。学习的重点是使用配置的方式实现
也可以使用配置的方式实现。学习的重点是使用配置的方式实现
Spring中事务控制的API/硬编码
PlatformTransactionManager 平台事务管理器 是一个接口
定义了开启事务、提交事务、回滚的方法
定义了开启事务、提交事务、回滚的方法
我们使用DataSourceTransactionManager这一实现类 主要针对
dbutils和jdbcTemplate 对jdbc的封装这个实现类就是一个切面类
dbutils和jdbcTemplate 对jdbc的封装这个实现类就是一个切面类
TransactionDefinition:定义事务参数的接口
TransactionStatus:事务运行状态接口
声明式/基于XML配置
通过配置文件告诉spring,让spring去使用事务控制,这种配置文件的方式实际上是对API的封装
导入依赖坐标
事务包spring-tx.jar
事务依赖包
AOP联盟
aspectj.jar
spring-aspects.jar
步骤
第一步:配置事务管理器并注入数据源
用<bean>标签配置DataSourceTransactionManager类,
该类需要一个数据源用ref属性配置
该类需要一个数据源用ref属性配置
<bean id="事务管理器名" class="事务类全路径">
<property name="" ref="bean标签中的数据源">
</bean>
<property name="" ref="bean标签中的数据源">
</bean>
例
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
第二步:配置事务的通知引用事务管理器
即声明事务
<!-- 事务的配置 -->
事务名 需要的事务管理器名
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<!-- 需要进行事务管理的方法 -->
<tx:method name="*" read-only="false" propagation="REQUIRED"></tx:method>
<tx:method name="find*" read-only="true" propagation="SUPPORTS"></tx:method>
</tx:attributes>
</tx:advice>
事务名 需要的事务管理器名
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<!-- 需要进行事务管理的方法 -->
<tx:method name="*" read-only="false" propagation="REQUIRED"></tx:method>
<tx:method name="find*" read-only="true" propagation="SUPPORTS"></tx:method>
</tx:attributes>
</tx:advice>
第三步:配置事务的属性
<!--在 tx:advice 标签内部 配置事务的属性 -->
<tx:attributes>
<!-- 指定方法名称:是业务核心方法
read-only:是否是只读事务。默认 false,不只读。
isolation:指定事务的隔离级别。默认值是使用数据库的默认隔离级别。
propagation:指定事务的传播行为。默认值是REQUIRED,表示一定会有事务,增删改的选择。
查询方法可以选择SUPPORTS。
timeout:指定超时时间。默认值为:-1。永不超时。
rollback-for:用于指定一个异常,当执行产生该异常时,事务回滚。产生其他异常,事务不回滚。
没有默认值,任何异常都回滚。
no-rollback-for:用于指定一个异常,当产生该异常时,事务不回滚,产生其他异常时,事务回
滚。没有默认值,任何异常都回滚。
<tx:attributes>
<!-- 指定方法名称:是业务核心方法
read-only:是否是只读事务。默认 false,不只读。
isolation:指定事务的隔离级别。默认值是使用数据库的默认隔离级别。
propagation:指定事务的传播行为。默认值是REQUIRED,表示一定会有事务,增删改的选择。
查询方法可以选择SUPPORTS。
timeout:指定超时时间。默认值为:-1。永不超时。
rollback-for:用于指定一个异常,当执行产生该异常时,事务回滚。产生其他异常,事务不回滚。
没有默认值,任何异常都回滚。
no-rollback-for:用于指定一个异常,当产生该异常时,事务不回滚,产生其他异常时,事务回
滚。没有默认值,任何异常都回滚。
第四步:配置AOP切入点表达式
<aop:config>
<aop:pointcut id="accountCut" expression="execution(* com.google.service.impl.*.*(..))"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="accountCut"></aop:advisor>
</aop:config>
<aop:pointcut id="accountCut" expression="execution(* com.google.service.impl.*.*(..))"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="accountCut"></aop:advisor>
</aop:config>
第五步:配置切入点表达式和事务通知的对应关系
<!-- 在aop:config标签内部:建立事务的通知和切入点表达式的关系 -->
<aop:advisor advice-ref="txAdvice" pointcut-ref="accountCut"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="accountCut"/>
基于注解配置
导入依赖jar包
半注解:
创建spring的配置文件导入约束并配置扫描的包
创建spring的配置文件导入约束并配置扫描的包
约束
DataAccess ctrl+f
DataAccess ctrl+f
步骤
xml配置事务管理器并注入数据源
在业务层使用@Transactional注解
可以在具体方法定义,优先级更高
在配置文件中开启spring对注解事务的支持
<!-- 开启 spring 对注解事务的支持 -->
<tx:annotation-driven transaction-manager="transactionManager"/>
<tx:annotation-driven transaction-manager="transactionManager"/>
注意:使用注解时不能继承JdbcDaoSupport了
需要自己定义private JdbcTemplate template,因为jar包中我们无法使用注解
需要自己定义private JdbcTemplate template,因为jar包中我们无法使用注解
纯注解
类似多了个
@EnableTransactionManagement
@EnableTransactionManagement
与其他一样
写一个类,用新注解标注,可以让该类的作用和application.xml的作用一样
@Configuration
细节:表名该被标注的类是一个配置类,但本质作用并不是标注它是一个配置类,而是加上这个注解之后,该类中的所有方法会被
CGLib代理,方法会跟原来的方法完全不一样,这样就保证了对象的生命周期和作用域。
如果不加该注解,在该类中用Bean注解照样好用,用AnnotationApplicationContext去容器取对象也好用,只不过如果该类
中的一个创建对象的方法调用了另一个创建对象的方法,那么另一个对象将被创建多次,不能保证对象的单例,即作用域scope
不能被保证
CGLib代理,方法会跟原来的方法完全不一样,这样就保证了对象的生命周期和作用域。
如果不加该注解,在该类中用Bean注解照样好用,用AnnotationApplicationContext去容器取对象也好用,只不过如果该类
中的一个创建对象的方法调用了另一个创建对象的方法,那么另一个对象将被创建多次,不能保证对象的单例,即作用域scope
不能被保证
@ComponentScan
通过该注解告诉spring在创建容器时需要扫描的包
属性value,和xml配置文件中的<context:component-scan>标签中的basePackage属性作用一样
@Bean
用于标注配置类中的方法,将方法的返回值对象存入spring容器中
name属性用于定义bean的id,当不写时,存入容器集合时key默认为方法名,值为对象
依然可以使用@Scope:定义类的单例多例
@PropertySource
用于加载.properties文件中的配置。例如我们配置数据源时,
可以把连接数据库的信息写到properties配置文件中,
就可以使用此注解指定properties配置文件的位置
可以把连接数据库的信息写到properties配置文件中,
就可以使用此注解指定properties配置文件的位置
属性: value[]:用于指定properties文件位置。
如果是在类路径下,需要写上classpath:
如果是在类路径下,需要写上classpath:
@Import
用于导入其他配置类,在引入其他配置类时,可以不用再写@Configuration注解。
细节:当我们使用注解配置方法时,如果方法有参数,spring会自动去容器中找有没有可用的bean,查找的方式与@Autowired
注解的方式一致
注解的方式一致
我们已经把要配置的都配置好了,但是新的问题产生了,由于没有配置文件了,如何获取容器呢?
此时不再使用ClassPathXmlApplicationContext,而是要用AnnotationConfigApplication,并将该配置类的class对象传入
此时不再使用ClassPathXmlApplicationContext,而是要用AnnotationConfigApplication,并将该配置类的class对象传入
基于编程式(了解)还没看
Springmvc
什么是springmvc
springmvc实际上是springframwork的一个模块,这个模块主要是对web的支持
springmvc是基于IoC和aop的
servlet缺点
获取参数比较繁琐,如果类型不是字符串类型则需要进行类型转换,还需要判断是否为空
如果每个servlet对应一个请求,则会产生大量的servlet,如果一个servlet对应多个请求,则违反可单一原则
原理
springmvc提供了一个servlet,我们把所有的请求都发送给这个servlet,这个servlet我们称之为核心控制器
核心控制器在tomcat启动时就会创建,在其init方法中会读取配置文件并扫描指定的包,会为由@Controller注解标记
的类创建对象,并把所有的由@RequestMapping注解标记的映射关系放到HandlerMapping中,键放注解的地址,值放
方法
的类创建对象,并把所有的由@RequestMapping注解标记的映射关系放到HandlerMapping中,键放注解的地址,值放
方法
当在地址栏输入地址,核心控制器会根据资源路径从HandlerMapping中寻找要执行的方法,找到后并调用
在控制类的方法中处理请求和响应,用return实现请求转发(携带的参数放到ModelMap中)和重定向(在返回的字符串前加redirect)
三大核心组件
处理器映射器
作用:帮我们找到对应的controller
HandlerMapping
传统开发方式,即配置文件方式
依赖BeanNameUrlHandlerMapping类
注解开发方式,已过时类
AnnotationMethodHandlerAdapter(过时的注解开发方式)
注解开发方式,最新类
RequestMappingHandlerAdapter(最新版本的注解开发方式)
在springmvc内部配置文件中,注解开发方式配置的还是过时的注解驱动类,需要在springmvc配置文件中用
<mvc:annotation-driven/>标签配置新版注解驱动类
<mvc:annotation-driven/>标签配置新版注解驱动类
处理器适配器
作用:帮我们找到响应的方法
方法返回ModelAndView
视图解析器
在springmvc配置文件中配置,用来配置contorller类中方法返回的字符串类型的前缀和后缀,简化返回的静态地址的字符串
将结果渲染成页面
控制类中的注解
@Controller
标记控制类,该类拥有处理请求的能力
@RequesMapping
标注方法,定义请求路径
窄化映射,可以定义到controller类上,隔离各个控制类中的方法
value和path属性
标注这个方法处理请求的地址,支持传数组,可以响应多个请求
method
设置接收的请求的请求方式
RequestMethod.GET, RequestMethod.POST
@ResponseBody
用此注解标记该方法,则底层会将该方法的返回值转换为json格式,spring没有内置转json的类,需要依赖三方jar包
@CrossOrigin
设置跨域访问
传统方式需要设置响应头setHeader("Access-Control-Allow-Origin", "协议+ip+端口")
浏览器的同源策略
同源指的是同一个服务器
如果a服务器向b服务器发送ajax请求,b服务器接收并响应数据,在默认情况下,a服务器的ajax拒绝接收b
服务器的响应,所以需要在b服务器端设置跨域访问,解决跨域错误问题
服务器的响应,所以需要在b服务器端设置跨域访问,解决跨域错误问题
@PathVaribale
用于绑定url的占位符,例如:在请求的url中,/emplist/{empId},{empId}就是一个占位符,在参数列表中想要对应占位符的参数前用
该注解标注,该注解中的值应当与占位符的值一致。url支持占位符是在spring3.0以后引入的
该注解标注,该注解中的值应当与占位符的值一致。url支持占位符是在spring3.0以后引入的
用于实现restful风格
如果想要实现restful风格,则需要将web.xml文件中核心控制器的请求路径设置为/,但此时将会把所有的静态文件例如js、css等
也作为请求发送到核心控制器并去找相应的方法执行,此时就会访问不到静态资源,所以释放静态资源
也作为请求发送到核心控制器并去找相应的方法执行,此时就会访问不到静态资源,所以释放静态资源
在springmvc配置文件中配置静态资源,用<mvc:resources location="请求地址例如:/js/(以js开头的请求)"
mapping="映射位置例如 :/js/**(项目下js文件夹下所有的文件的子文件)" />
mapping="映射位置例如 :/js/**(项目下js文件夹下所有的文件的子文件)" />
restful风格
用占位符的方式接收参数,占位符的参数名叫啥,前端name应该叫啥
传统风格
RequestMapping("/delete")
localhost:8080/delete?id=10
localhost:8080/delete?id=10
restful风格
RequestMapping("/delete/{id}")
restful风格:localhost:8080/delete/10
restful风格:localhost:8080/delete/10
@ModelAttribute
被该注解标注的方法会先执行
适用场景:当前端提交表单,带着表单数据向控制类中的某个方法发送请求,但表单数据并不是一个完整的JavaBean对象的
数据,此时可以定义一个新的方法,用该注解标注,那么在执行对应请求方法时会先执行该方法,该方法也可以从请求中
获取请求参数,可以在该方法中通过请求参数从数据库查询完整数据,并将最后JavaBean对象返回,这样数据就会完整,另
一种方式是无返回值方法,可以参参数列表定义一个map集合,将最后的JavaBean放入map集合中,在对应的请求方法的
参数列表中也用ModelAttribute注解标注参数,并在注解中给出放入map集合的key
数据,此时可以定义一个新的方法,用该注解标注,那么在执行对应请求方法时会先执行该方法,该方法也可以从请求中
获取请求参数,可以在该方法中通过请求参数从数据库查询完整数据,并将最后JavaBean对象返回,这样数据就会完整,另
一种方式是无返回值方法,可以参参数列表定义一个map集合,将最后的JavaBean放入map集合中,在对应的请求方法的
参数列表中也用ModelAttribute注解标注参数,并在注解中给出放入map集合的key
注解语法糖
在spring4.2.x版本及以后出现了复合注解
@GetMapping("/")
@PostMapping
直接指明请求方式
@RestController
@Controller和@ResponseBody的符合注解
参数绑定
默认参数绑定
request
response
session
ModelMap
基本数据类型
在对应方法的参数列表中定义请求参数
类型写你需要的,底层会帮你转,要求参数列表中的参数名与请求参数名一致
原理
mvc会反射你的方法参数列表,根据参数名去找请求参数对应的值,会尝试将数据转成你想要的类型,
如果不能转成你想要的,抛异常
如果不能转成你想要的,抛异常
bean方式
可以使用对象接收,在参数列表定义对象类型,mvc可以直接自动封装成对象,前提是对象的属性名跟请求参数名一致
绑定包装的bean
即对象中有对象引用,要求前端数据的name为bean中的bena名称.属性名,例如订单实体中有商品实体(pro),如果要绑定商品
id,则在前端需要将参数name定义为pro.id
id,则在前端需要将参数name定义为pro.id
数组绑定
一般用于批量删除,在前端定义复选框,复选框的名称相同且和控制类对应方法的数组名一致,springmvc可自动帮你获取参数
集合绑定
应用场景不多,一般用于批量修改,前端修改n条数据,提交多个对象到后台,但只能支持向对象中接收集合,即
控制类对应方法中需要定义一个集合,接收参数时会接收到该对象的集合中,而且要求前端name属性为集合名[下标].对象属性名
在jsp页面的c:foreach标签中的status属性可以获取遍历的集合的每次索引值
控制类对应方法中需要定义一个集合,接收参数时会接收到该对象的集合中,而且要求前端name属性为集合名[下标].对象属性名
在jsp页面的c:foreach标签中的status属性可以获取遍历的集合的每次索引值
注解方式
@RequestParam注解,标记参数列表
注解属性
value/name
指明要获取的参数名,用于跟请求参数名匹配
required
默认为true,要求请求参数必须有,如果没有,则出现400,设为false则可以没有
defaultValue
用于定义参数列表的默认值,如果请求参数没有传来,则默认值生效
自定义转换器
当前端参数出现springmvc无法自动转换的参数时,例如时间,可以使用自定义转换器
步骤
创建一个类,实现Converter<T, V>,T:源,V:目标,即需要将什么类型转换为什么类型
实现convert(T t)方法并返回想要的
将自定义转换器配置到springmvc容器中
在注册新版处理器映射器,处理器适配器驱动时,将自定义转换器配置
<mvc:annotation-driven conversion-service="自定义转换器id"/>
<mvc:annotation-driven conversion-service="自定义转换器id"/>
配置自定义转换器
<bean id="" class="org.springframework.format.support.FormattingConversionServiceFactoryBean">
<property name="convers">
<set><bean class="自己定义的转换器类的全限定名"/></set>
</property>
</bean>
<bean id="" class="org.springframework.format.support.FormattingConversionServiceFactoryBean">
<property name="convers">
<set><bean class="自己定义的转换器类的全限定名"/></set>
</property>
</bean>
注解方式解决mvc不支持的参数绑定
将spring不支持的绑定类型参数上用@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
springmvc如何响应
返回String类型的地址
在方法中直接return页面地址默认就是请求转发
在返回的页面地址字符串前加redirect:则为重定向
请求转发携带的参数需要放到ModelMap中
例如
配置视图解析器
在spring配置文件中配置前缀和后缀
配置语法
将配置文件的前缀和后缀与控制类中方法return的字符串拼接即可得到想要的路径
拦截器
框架提供的有跟过滤器功能类似但更强大的拦截器
拦截器会拦截Controller类中方法的调用
在controller类中方法执行前被拦截
在controller类的方法执行之后但在视图解析前被拦截
方法执行完且视图解析之后拦截
快速入门
1. 定义一个类实现HandlerInterceptor接口
2. 实现该接口中的方法,所有的方法
返回值为boolean,真放行,假拦截
返回值为boolean,真放行,假拦截
该接口中的方法为默认方法,每个方法都有不同的拦截时机
preHandle(req,resp)
在controller的方法被调用之前执行该方法
postHandle(req, resp)
在方法被调用视图解析之前调用
afterCompletion(req, resp)
在方法执行完视图解析之后调用
3. 在配置文件中配置拦截器,可以配置多个拦截器,
哪个在上边,最先执行哪个拦截器
哪个在上边,最先执行哪个拦截器
<mvc:interceptors>
<mvc:interceptor>
<!-- 配置哪个方法需要拦截 -->
<mvc:mapping path="/**">
<!-- 配置哪个方法不需要拦截 -->
<mvc:exclued-mapping path="">
<!-- 设置拦截器类路径 -->
<bean class="拦截器类全限定名">
</mvc:interceptor>
</mvc:interceptors>
<mvc:interceptor>
<!-- 配置哪个方法需要拦截 -->
<mvc:mapping path="/**">
<!-- 配置哪个方法不需要拦截 -->
<mvc:exclued-mapping path="">
<!-- 设置拦截器类路径 -->
<bean class="拦截器类全限定名">
</mvc:interceptor>
</mvc:interceptors>
执行顺序
两个兰拦截器,1拦截器配置在前,2拦截器配置在后
文件上传
springmvc文件上传原理
浏览器通过input标签将需要上传的文件自动读入到内存,当提交表单的时候,会将该文件发送到后台核心控制器,在请求中
会携带这个文件,此时会调用到文件解析器,文件解析器会解析请求对象,将请求对象的文件解析出来返回给核心控制器
会携带这个文件,此时会调用到文件解析器,文件解析器会解析请求对象,将请求对象的文件解析出来返回给核心控制器
核心控制器再调用处理器映射器,找到相应的控制类的方法,通过参数绑定的形式,绑定给该方法,该方法的参数类型必须
是MultipartFile类型,参数名必须要和input标签中的name属性保持一致,最后调用该MultipartFile对象的方法进行上传
是MultipartFile类型,参数名必须要和input标签中的name属性保持一致,最后调用该MultipartFile对象的方法进行上传
文件解析器配置
前后端要求
页面要求
传统表单方式
1. 表单提交方式一定是post
2. 表单的enctype的值一定是multipart/form-data
3. input的type一定是file
ajax方式
1. type:post
2. data:FormData
3. processData: false
4. contentType: false,
springmvc要求
1. 需要两个jar包commons-io.jar和commons-fileupload.jar
2. 在ppringmvc配置文件中配置文件解析器
3. 绑定参数类型一定为MultipartFile,参数名字要和input的name属性的值保持一致
springmvc和struts2优劣
相同点
都基于mvc设计模式
底层都是对ServletAPI的封装
处理请求的机制都是一个核心控制器
区别
springmvc的入口是Servlet,struts2的入口是Filter
springmvc的最小单元是方法,是基于方法设计的,struts2的最小单元是基于类,每次执行都会创建一个动作类,所以mvc更快
springmvc使用更简洁,发送ajax更方便
s2的OGNL表达式使页面开发效率更高,但执行效率没有并没有比JSTL有所提升
Spring JDBC
Spring框架对JDBC的简单封装。提供了一个JDBCTemplate对象简化JDBC的开发
使用步骤
1、导入依赖坐标
2、配置xml或者使用properties
JdbcTemplate依赖于DataSource
配置数据源
c3p0
DBCP
Druid
Spring 内置数据源
配置JdbcTemplate
注入数据源
注入数据源
3、使用IOC创建对象
4、在Dao中调用JdbcTemplate的方法来完成CRUD的操作
update():执行DML语句。增、删、改语句
queryForMap():查询结果将结果集封装为map集合,
将列名作为key,将值作为value 将这条记录封装为一个map集合
* 注意:这个方法查询的结果集长度只能是1
* 注意:这个方法查询的结果集长度只能是1
String sql = "select * from emp where id = ? or id = ?";
Map<String, Object> map = template.queryForMap(sql, 1001,1002);
System.out.println(map);
Map<String, Object> map = template.queryForMap(sql, 1001,1002);
System.out.println(map);
queryForList():查询结果将结果集封装为list集合
String sql = "select * from emp";
List<Map<String, Object>> list = template.queryForList(sql);
for (Map<String, Object> stringObjectMap : list) {
System.out.println(stringObjectMap);
}
}
List<Map<String, Object>> list = template.queryForList(sql);
for (Map<String, Object> stringObjectMap : list) {
System.out.println(stringObjectMap);
}
}
query():查询结果,将结果封装为JavaBean对象
query的参数:RowMapper
* 一般我们使用BeanPropertyRowMapper实现类。可以完成数据到JavaBean的自动封装
* new BeanPropertyRowMapper<类型>(类型.class)
* 一般我们使用BeanPropertyRowMapper实现类。可以完成数据到JavaBean的自动封装
* new BeanPropertyRowMapper<类型>(类型.class)
String sql = "select * from emp";
List<Emp> list = template.query(sql, new BeanPropertyRowMapper<Emp>(Emp.class));
for (Emp emp : list) {
System.out.println(emp);
}
}
List<Emp> list = template.query(sql, new BeanPropertyRowMapper<Emp>(Emp.class));
for (Emp emp : list) {
System.out.println(emp);
}
}
用户信息查询
queryForObject:查询结果,将结果封装为对象
一般用于聚合函数的查询
用户登陆验证
用户登陆验证
String sql = "select count(id) from emp";
//一般都用Long接收
Long total = template.queryForObject(sql, Long.class);
System.out.println(total);
//一般都用Long接收
Long total = template.queryForObject(sql, Long.class);
System.out.println(total);
多个Dao间抽取BaseCode
比如抽取这段每个Dao重复的代码
private JdbcTemplate template;
public void setTemplate(JdbcTemplate template){
this.template=template;
}
private JdbcTemplate template;
public void setTemplate(JdbcTemplate template){
this.template=template;
}
让dao继承JdbcDaoSupport
JdbcDaoSupport是spring框架为我们提供的一个类,
该类中定义了一个JdbcTemplate对象,我们可以直接获取使用,自己就不需要配置JdbcTemplate
但是要想创建该对象,需要为其配置提供一个数据源dataSource
该类中定义了一个JdbcTemplate对象,我们可以直接获取使用,自己就不需要配置JdbcTemplate
但是要想创建该对象,需要为其配置提供一个数据源dataSource
配置了dataSource 就会调用JdbcDaoSupport中的setDataSource方法,
该方法会创建一个JdbcTemplate对象,具体看源码
该方法会创建一个JdbcTemplate对象,具体看源码
注意:使用注解时不能继承JdbcDaoSupport了
需要自己定义private JdbcTemplate template,
因为jar包中我们无法使用注解
需要自己定义private JdbcTemplate template,
因为jar包中我们无法使用注解
ssm整合
在web.xml中配置
配置核心控制器
核心控制器路径,此处的url-pattern如果是/则指的是除jsp文件以外的所有请求都包含,/*则包含jsp
核心控制器创建时机
核心控制器需要的配置文件
配置监听器
当服务器启动时,该监听器会通过配置文件来初始化spring容器,实现ioc
如果想通过配置监听来实现ioc,需要配下方的配置全局参数
配置全局参数
该参数指向spring配置文件(该配置文件用于整合spring、mybatis),监听器会加载该配置文件,将配置文件
中的数据源、对象以及mapper扫描器创建
中的数据源、对象以及mapper扫描器创建
持久层(dao)
mybatis.xml(可有可无),一般在该文件下配置settings和typeAliases,不过在配置sqlFactoryFactoryBean时也可以配置别名
applicationContext-dao.xml
数据源(druid)
sqlSessionFactory
数据源
别名
<property name="typeAliasesPackage" value="com.wxs.entity">
配置mapper扫描器
在配置sqlSession工厂时,通过property标签,让name值为mapperLocations
value值为classpath:com/wxs/mapper,这里需要使用斜线
value值为classpath:com/wxs/mapper,这里需要使用斜线
用这种方式可以扫描项目下任意路径,可以解决mapper映射器文件和接口不在同意路径下的问题
mapper扫描器
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
在该bean标签下配置sqlSessionFactoryBean,因为mapper接口的底层还是需要由sqlSession得到的
配置需要扫描的包
业务层
applicationContext-service.xml
事务
包扫描器(主要针对@Service注解)
web层(有mvc支持)
springmvc.xml
注解驱动版本
<mvc:annotation-driven/>
配置处理器适配器、处理器映射器最新驱动
包扫描器,主要针对@Controller注解
视图解析器
以setter方式注入前缀和后缀
异常处理器,文件解析器,拦截器
***父子容器关系
spring容器(配置文件)与springmvc容器(配置文件)是父子容器关系
在这两个容器中都只能出现一个<context:property-placeholder location="">
标签加载文件,且子容器可以访问父容器加载到的配置文件,而父容器不能访问子容器加载的文件
在这两个容器中都只能出现一个<context:property-placeholder location="">
标签加载文件,且子容器可以访问父容器加载到的配置文件,而父容器不能访问子容器加载的文件
什么是Spring
Spring是一个轻量级的IOC和AOP容器的开源框架
Spring提倡以最少侵入(可以理解为耦合度)的方式来管理应用中的代码
Spring体系结构
子主题
IOC
概念
控制反转
控制:对对象的创建和管理
反转:对对象的控制由程序员转变为框架
核心目的
让spring来管理对象
优点
单例
降低耦合(类与类之间的依赖关系)
IoC核心容器
顶层接口BeanFactory和
ApplicationContext的区别
ApplicationContext的区别
不管service还是dao一般不会有类成员,不会有线程安全问题,选择使用单例模式创建
既然是单例模式创建,采用立即加载创建对象就行
既然是单例模式创建,采用立即加载创建对象就行
单例对象适用
AplicationContext在构建核心容器时,创建对象采用的是立即加载的方式,即配置文件一读取完,立马创建对象
AplicationContext在构建核心容器时,创建对象采用的是立即加载的方式,即配置文件一读取完,立马创建对象
多例对象适用
BeanFactory在构建核心容器时,创建对象采用的是延迟加载方式,即什么时候通过Id获取对象getBean(id),什么时候创建
BeanFactory在构建核心容器时,创建对象采用的是延迟加载方式,即什么时候通过Id获取对象getBean(id),什么时候创建
ApplicationContext常用实现类
ClassPathXmlApplicationContext
加载类路径下的配置文件
FileSystemXmlApplicationContext
加载磁盘任意位置的配置文件
AnnotationConfigApplicationContext
用于使用新注解+配置类的方式代替xml配置文件时
ApplicationContext在构建核心容器是,创建对象采用的是立即加载
IOC实例化Bean对象三种方式
使用默认构造函数创建
在spring配置文件中用bean标签配置
bean标签的属性
id
创建的对象名
class
需要被管理的类的全路径
spring管理实例工厂-使用实例工厂的方法创建对象
条件:需要工厂类,这个工厂类中需要有普通方法
配置文件语法
<bean id="factory" class="com.google.factory.BeanFactory" />
<bean id="car" factory-bean="factory" factory-method="普通方法名">
需要先创建工厂对象再调用工厂中的普通方法
spring管理静态工厂-使用静态工厂的方法创建对象
条件:需要工厂类,该工厂类中需要有静态方法
配置文件语法
<bean id="factory" class="com.wxs.factory.BeanFactory" factory-method="静态方法名" />
当用spring容器调用getBean方法时会创建工厂类对象,并执行工厂类中的方法返回需要的对象并放入spring容器中
IOC三种开发方式
手动配置(纯XML的IOC配置)
bean标签:在配置文件中用bean标签配置一个类
实例化Bean的三种方式
id属性:用来给对象命名
class属性:用于写实体类的全路径名,供反射适用
bean作用范围:scope属性
singleton
单例(默认值)
只要配置文件被加载,就会创建对象,创建的对象放在spring容器中,这个容器底层为
map集合,key为bean标签的id值,value为这个对象
map集合,key为bean标签的id值,value为这个对象
当调用容器的getBean方法的时候,总是获取到唯一的实例
service/dao需要单例模式
proyotype
多例
当配置文件加载的时候不创建对象,当调用容器的getBean方法时,创建对象并返回,调用一次getBean则创建一次
action适合多例
request
作用于web应用的请求范围
session
作用于web应用的会话范围
globle-session
作用于集群环境的会话范围(全局范围)
bean生命周期
单例对象:scope="singleton"
一个应用只有一个对象的实例。它的作用范围就是整个引用。
生命周期:
对象出生:当应用加载,创建容器时,对象就被创建了。
对象活着:只要容器在,对象一直活着。
对象死亡:当应用卸载,销毁spring容器时,对象就被销毁
生命周期:
对象出生:当应用加载,创建容器时,对象就被创建了。
对象活着:只要容器在,对象一直活着。
对象死亡:当应用卸载,销毁spring容器时,对象就被销毁
多例对象:scope="prototype"
每次访问对象时,都会重新创建对象实例。
生命周期:
对象出生:当使用对象时,创建新的对象实例。对象活着:只要对象在使用中,就一直活着。
对象死亡:当对象长时间不用时,被 java 的GC机制回收
生命周期:
对象出生:当使用对象时,创建新的对象实例。对象活着:只要对象在使用中,就一直活着。
对象死亡:当对象长时间不用时,被 java 的GC机制回收
DI(依赖注入)的方式
有参构造器注入
条件:必须要有构造器
bean标签下<constructor-arg>标签
index:指定参数在构造函数参数列表的索引位置
type:指定参数在构造函数中的数据类型
常用:name:属性名
=======上面三个都是找给谁赋值,下面两个指的是赋什么值的=======
value:值,针对基本类型和Stirng
ref:针对其他bean类型,也就是说,必须得是在配置文件中配置过的bean
setter注入(常用)
条件:属性必须有setter方法
bean标签下<property>标签
name:属性名(不是变量名而是set方法小写的那个名字)
value:属性值,针对基本类型和String类型
ref:针对对象类型,指向的是bean标签的id属性的值
复杂注入(集合注入)
list类型语法
set类型
array类型
数组类型与list类型类似只是没有ref标签
map类型
<bean id="empService" class="com.google.service.EmpService">
<property name="list">
<map>
<entry key="" value=""></entry>
</map>
</property>
</bean>
<property name="list">
<map>
<entry key="" value=""></entry>
</map>
</property>
</bean>
properties类型
<bean id="empService" class="com.google.service.EmpService">
<property name="p">
<props>
<prop key="" >value值只能写标签体里</prop>
</props>
</property>
</bean>
<property name="p">
<props>
<prop key="" >value值只能写标签体里</prop>
</props>
</property>
</bean>
结构相同,标签可以互换
list、array、set
props、map
p名称空间注入
条件:需要在配置文件中导入p的命名空间(spring提供的),底层还是set方式,所以属性也必须有setter方法
property子标签<list>
专门用于给list集合类型的成员变量赋初始值
格式
<bean id="empService" class="com.google.service.EmpService">
<property name="list">
<list>
<value>11</value>
</list>
</property>
</bean>
<property name="list">
<list>
<value>11</value>
</list>
</property>
</bean>
半自动配置方式
半自动配置方式主要是将类与类之间的依赖关系用注解的方式实现
类的对象还是需要手动配置,但类的依赖关系用注解@Autowired自动实现,
例如A类中引用了B类,在配置文件中用bean标签配置A,在A中用注解标记引用的B
例如A类中引用了B类,在配置文件中用bean标签配置A,在A中用注解标记引用的B
***注意:Spring框架为了效率,默认在扫描类的时候不会扫描注解,所以默认情况下只加Autowired注解是无法
自动注入的,需要在配置文件用<context:annotation-config/ >进行配置
自动注入的,需要在配置文件用<context:annotation-config/
全自动配置(纯注解的IOC配置)
基本不用配置文件
基于注解整合时,导入约束时需要多导入一个context名称空间下的约束。
四个实例化对象的注解
@Component:无法划分类的时候
@Repository:一般用于持久层
@Service:业务层
@Controller:控制层/表现层(springmvc替代servlet)
====以上四个注解没有区别,只是用于不同场景====
bean作用范围
@Scope:定义类的单例多例
生命周期(了解)
@PostConstruct
指定初始化方法
@PreDestory
指定销毁方法
三个依赖注入的注解
@Autowired
该注解由Spring框架提供
自动按照类型注入,如果找不到就是没有,如果找到多个再按照变量名匹配,如果变量名不匹配最后报错
解决方式是搭配Qualifier注解指明适用哪个注解
解决方式是搭配Qualifier注解指明适用哪个注解
@Qualifier
@Resource
该注解由JDK提供
先按名字去找,第一种是name属性配置名字@Resource(name = "名字"),如果没有指定名字
则把变量名当作要寻找的属性名,如果再找不到,最后按类型去找
则把变量名当作要寻找的属性名,如果再找不到,最后按类型去找
@Value
针对基本数据类型和String类型,且可以使用Spring中SqEl(Spring中的el表达式)
${表达式}
${表达式}
在类上用@Component注解标记,类中的依赖关系用@Autowired注解标记
在配置文件中用<context:component-scan base-package="com.google" />配置需要扫描的包
在全自动配置方式中,对于私有的成员变量,也无须提供setter方法,反射会自动打开访问权限强制访问
***注意:如果通过一个类型匹配到了多个实现类,则会报错,解决方式为在引用类型上添加@Qualifier("")注解指明需要的类
spring新注解(配置我们无法修改的类和配置扫描注解的类路径)
写一个类,用新注解标注,可以让该类的作用和application.xml的作用一样
@Configuration
细节:表名该被标注的类是一个配置类,但本质作用并不是标注它是一个配置类,而是加上这个注解之后,该类中的所有方法会被
CGLib代理,方法会跟原来的方法完全不一样,这样就保证了对象的生命周期和作用域。
如果不加该注解,在该类中用Bean注解照样好用,用AnnotationApplicationContext去容器取对象也好用,只不过如果该类
中的一个创建对象的方法调用了另一个创建对象的方法,那么另一个对象将被创建多次,不能保证对象的单例,即作用域scope
不能被保证
CGLib代理,方法会跟原来的方法完全不一样,这样就保证了对象的生命周期和作用域。
如果不加该注解,在该类中用Bean注解照样好用,用AnnotationApplicationContext去容器取对象也好用,只不过如果该类
中的一个创建对象的方法调用了另一个创建对象的方法,那么另一个对象将被创建多次,不能保证对象的单例,即作用域scope
不能被保证
@ComponentScan
通过该注解告诉spring在创建容器时需要扫描的包
属性value,和xml配置文件中的<context:component-scan>标签中的basePackage属性作用一样
@Bean
用于标注配置类中的方法,将方法的返回值对象存入spring容器中
name属性用于定义bean的id,当不写时,存入容器集合时key默认为方法名,值为对象
依然可以使用@Scope:定义类的单例多例
@PropertySource
用于加载.properties文件中的配置。例如我们配置数据源时,
可以把连接数据库的信息写到properties配置文件中,
就可以使用此注解指定properties配置文件的位置
可以把连接数据库的信息写到properties配置文件中,
就可以使用此注解指定properties配置文件的位置
属性: value[]:用于指定properties文件位置。
如果是在类路径下,需要写上classpath:
如果是在类路径下,需要写上classpath:
@Import
用于导入其他配置类,在引入其他配置类时,可以不用再写@Configuration注解。
细节:当我们使用注解配置方法时,如果方法有参数,spring会自动去容器中找有没有可用的bean,查找的方式与@Autowired
注解的方式一致
注解的方式一致
我们已经把要配置的都配置好了,但是新的问题产生了,由于没有配置文件了,如何获取容器呢?
此时不再使用ClassPathXmlApplicationContext,而是要用AnnotationConfigApplication,并将该配置类的class对象传入
此时不再使用ClassPathXmlApplicationContext,而是要用AnnotationConfigApplication,并将该配置类的class对象传入
原理
如何创建对象
通过IO读取配置文件
利用反射创建对象:class.forName(配置文件中的全路径名)
如果是全自动模式,则通过IO读取配置文件读到的是包,然后再通过IO去扫描这个包下所有的类(包括子包)
扫描的类如果由Component注解标记,则创建该类对象,如果没有,则忽略
扫描的类如果由Component注解标记,则创建该类对象,如果没有,则忽略
重点是反射+IO
如何管理依赖关系
扫描所有成员变量,如果成员变量带有自动注入注解,则从自己容器中寻找要注入的对象,利用反射对其进行注入,如果
找到相应对象,暴力破解直接赋值,如果没找到,则报错
找到相应对象,暴力破解直接赋值,如果没找到,则报错
Spring整合junit
在spring下junit的问题
程序的入口为main
junit单元测试中没有main方法也能执行
junit内部集成了一个main方法
当执行时,会利用反射判断该测试类有没有被@Test标注的方法
如果有,.invoke执行该方法
junit不会管我们是否用框架
在执行测试方法的时候,junit根本不知道我们是否使用了框架,所以在执行的时候根本不会为我们通过配置文件或者
配置类来创建spring容器
配置类来创建spring容器
所以,在用junit测试的时候根本没有ioc容器,就算使用Autowired方法也不会有效果
整合思路
导入spring整合junit的jar包--->spring-test.jar
使用junit提供的一个注解,把原有的main方法替换了,替换成spring自己的main
@RunWith(SpringJUnit4ClassRunner.class)
@RunWith(SpringJUnit4ClassRunner.class)
告知spring运行器,spring和ioc创建是基于xml还是注解,并说明位置
用@ContextConfiguration--->locations属性:指定xml文件位置,加上classpath关键字,表示该文件在类路径下
classes属性:指定配置类所在位置
用@ContextConfiguration--->locations属性:指定xml文件位置,加上classpath关键字,表示该文件在类路径下
classes属性:指定配置类所在位置
代码
当我们用spring5.x版本的时候,要求junit版本在4.12及以上版本
开发模式
jsp开发模式
开发效率较高
执行效率与交互效果低
每次请求都是整个页面刷新,当页面数据量大,用户设备老旧或者网速较差,会出现页面卡顿问题
以至于交互效果较差
以至于交互效果较差
不灵活,解决多端变化问题较难
如果有多个前端页面例如手机版、电脑版、iPad版,那得写多套控制层,较难
前后端分离
后台一律响应数据(json格式)而不响应页面,前端利用前端语言和后台的网络接口进行接收数据和显示
优点
灵活,易于解决多端变化问题
只需要写一套后台,不同前端用不同方式与后台交互
缺点
开发效率较低
前后端分离开发模式开发前需要先设计文档,规定后台与前端所需要接口和参数的标准格式,以便于前后端同时开发且
不容易出现前端调用后台接口出问题的情况
不容易出现前端调用后台接口出问题的情况
跨域问题
跨域带数据
跨域带数据
同源策略
同源
协议相同、ip相同、端口号相同
DOM 同源策略:禁止对不同源页面 DOM 进行操作。
这里主要场景是 iframe 跨域的情况,不同域名的 iframe 是限制互相访问的。
这里主要场景是 iframe 跨域的情况,不同域名的 iframe 是限制互相访问的。
做一个假网站,里面用 iframe 嵌套一个银行网站 http://mybank.com。
把 iframe 宽高啥的调整到页面全部,这样用户进来除了域名,
别的部分和银行的网站没有任何差别。
别的部分和银行的网站没有任何差别。
这时如果用户输入账号密码,我们的主网站可以跨域访问到
http://mybank.com 的 dom 节点,就可以拿到用户的账户密码了。
http://mybank.com 的 dom 节点,就可以拿到用户的账户密码了。
XMLHttpRequest 同源策略:
禁止使用 XHR 对象向不同源的服务器地址发起 HTTP 请求。
禁止使用 XHR 对象向不同源的服务器地址发起 HTTP 请求。
在不同源情况下,一个服务器向另一个服务器发送ajax
请求,浏览器默认是禁止的,,会产生跨域错误
请求,浏览器默认是禁止的,,会产生跨域错误
解决方案
1. 设置后台允许接受跨域请求
在后端设置响应头
response.setHeader("Access-Control-Allow-Origin", "http://localhost:63342");
该服务器路径可以通过请求对象动态获取
request.getHeader("Origin")
或者通过注解设置
在每个Controller类上加@CrossOrigin注解,该注解允许请求服务器默认为*
但是当前端设置为允许跨域携带参数后不允许将跨域访问路径设为*,虽然该注解可以设置路径,但需要在每个注解中设置
跨域请求服务器路径,所以该方式不太方便
跨域请求服务器路径,所以该方式不太方便
通过拦截器设置
在mvc.xml配置文件中配置拦截器
将该拦截器设置在所有拦截器的最上方,所有请求来之后都先被该拦截器拦截
在拦截器类中为response设置响应头
response.setHeader("Access-Control-Allow-Origin", "http://localhost:63342");
response.setHeader("Access-Control-Allow-Origin", "http://localhost:63342");
2. 设置前端允许跨域请求携带数据
在前端页面设置xhr对象的属性
在ajax请求中设置属性
xhrFields:{
withCredentials:true
},
withCredentials:true
},
XHR原生对象的withCredentials是用于跨域请求的,默认为false
如果想要跨域请求并携带数据则需要将其打开
如果想要跨域请求并携带数据则需要将其打开
***在这种可携带数据的跨域模式下不可以设置为*
3. 后台允许跨域请求携带数据
在拦截器类中设置响应头
response.setHeader("Access-Control-Allow-Credentials", "true");
Spring5新特性(了解)
0 条评论
下一页