-
什么是接口?
查看全部 -
为什么使用框架
查看全部 -
什么是框架?
查看全部 -
Spring适用范围
查看全部 -
Spring作用
查看全部 -
Spring Framework Runtime
查看全部 -
Spring是什么
查看全部 -
AspectJ介绍及Pointcut注解应用
1、@AspectJ的风格类似纯java注解的普通java类。
2、Spring可以使用AspectJ来做切入点解析。
3、AOP的运行时仍旧是纯的SpringAOP,对AspectJ的编译器或者织入无依赖性。
Spring中配置@AspectJ
1、对@AspectJ支持可以使用XML或者Java注解类型的配置(@EnableAspectJAutoProxy使用AspectJ进行自动代理)
2、前提:确保AspectJ的aspectjweaver.jar库包含在应用程序(版本1.6.8或更高版本)的classpath中,以Spring4.0.5为基准的,其他版本可能不一样(可以根据具体情况查找Spring具体文档)。
aspect注解的具体使用
1、@AspectJ切面使用@Aspect注解配置,拥有@Aspect的任何bean将被Spring自动识别并应用。
2、@Aspect注解的类可以有方法和字段,他们也可能包括切入点(pointcut),通知(Advice)和引入(introduction)声明。
3、@Aspect注解是不能够通过类路径自动检测发现的,所以需要配合使用@Component注释或者在xml配置bean(@Aspect注解的类配置在xml中)。
4、一个类中的@Aspect注解标识它为一个切面,并且将自己从自动代理中排出。
如何定义一个切入点
1、一个切入点通过一个普通的方法定义来提供,并且切入点表达式使用@Pointcut注解,方法返回类型必须为void
2、定义一个名为'anyOldTransfer',这个切入点将匹配任何名为“transfer”的方法的执行。
切入点支持哪些定义方式
组合Pointcut
1、切入点表达式可以通过&&、||和!进行组合,也可以通过名字引用切入点表达式。
2、通过组合,可以建立更加复杂的切入点表达式。
如何定义良好的pointcuts
1、AspectJ是编译期的AOP
2、检查代码并匹配连接点与切入点的代价是昂贵的。
3、一个好的切入点应该包括以下几点
【a】选择特定类型的连接点,如:execution,get,set,call,handler。
【b】确定连接点范围,如:within,withincode。
【c】匹配上下文信息,如:this,target,@annotation。
查看全部 -
ProxyFactoryBean及相关内容(下)
Proxying classes
1、前面的例子中如果没有Person(BizLogic)接口,这种情况下Spring会使用CGLIB代理,而不是JDK动态代理。
2、可以强制使用CGLIB,即使有接口的情况下。
3、CGLIB代理的工作原理是在运行时生成目标类的子类,Spring配置这个生成的子类委托方法调用到原来的目标
4、子类是用来实现Decorator模式,织入通知
5、CGLIB的代理对用户是透明的,需要注意:
【1】final方法不能被通知,因为它们不能被覆盖。
【2】不用把CGLIB添加到classpath中,在Spring3.2中,CGLIB被重新包装并包含在Spring核心JAR(即基于CGLIB的AOP就像JDK动态代理一样“开箱即用”,也就是说不用手动引入CGLIB这个包)
使用global advisors:使*做通配符,匹配所有拦截器加入通知链
实现了MethodInterceptor这个类似的方式,才可以使用通配符的方式。
简化的proxy定义
1、Spring中支持父子bean定义的方式,以及内部bean定义,可以带来更简洁的代理定义(抽象属性标记父bean定义为抽象的这样它不能被实例化)
Spring官方文档给出的例子(TransactionProxyFactoryBean是基于事物的代理Bean)通过getBean获得的是TransactionProxyFactoryBean的实例
使用ProxyFactory的好处:
1、使用SpringAOP而不必依赖于SpringIOC
2、大多数情况下最佳实践是用IoC容器创建AOP代理。
3、虽然可以硬编码方式实现,但是Spring推荐使用配置或注解方式实现。
使用auto-proxy
1、Spring也允许使用“自动代理”的bean定义,它可以自动代理选定的bean,这是建立在Spring的“bean post processor”功能基础上的(在加载bean的时候就可以修改)
2、主要通过BeanNameAutoProxyCreator实现(自动代理所有以jdk开始的Bean,也包括onlyJdk这种Bean)
3、DefaultAdvisorAutoProxyCreator,当前IoC容器中自动应用,来达到创建动态代理的效果,不用显示声明引用advisor的bean定义
查看全部 -
ProxyFactoryBean及相关内容(下)
Proxying classes
1、前面的例子中如果没有Person(BizLogic)接口,这种情况下Spring会使用CGLIB代理,而不是JDK动态代理。
2、可以强制使用CGLIB,即使有接口的情况下。
3、CGLIB代理的工作原理是在运行时生成目标类的子类,Spring配置这个生成的子类委托方法调用到原来的目标
4、子类是用来实现Decorator模式,织入通知
5、CGLIB的代理对用户是透明的,需要注意:
【1】final方法不能被通知,因为它们不能被覆盖。
【2】不用把CGLIB添加到classpath中,在Spring3.2中,CGLIB被重新包装并包含在Spring核心JAR(即基于CGLIB的AOP就像JDK动态代理一样“开箱即用”,也就是说不用手动引入CGLIB这个包)
使用global advisors:使*做通配符,匹配所有拦截器加入通知链
查看全部 -
ProxyFactoryBean及相关内容(上)
1、通过Spring API创建Spring AOP代理的基本方法是使用org.springframework.aop.framework.ProxyFactoryBean
2、ProxyFactoryBean可以完全控制切入点和通知(advice)以及他们的顺序。(和其它工厂Bean实现方式是一样的,都引入了一个中间层)
假如有这样一种情况:定义了一个Bean的Id为foo的ProxyFactoryBean,那么引用foo这个对象,看到的将不是ProxyFactoryBean本身,而是ProxyFactoryBean这个类通过getObject()方法创建的对象。
getObject():将创建一个AOP代理包装一个目标对象。(ProxyFactoryBean通过这种方式达到代理的目的)
3、使用ProxyFactoryBean或者其它IoC相关类来创建AOP代理的最重要好处是因为通知和切入点也可以由IoC来管理。
4、当被代理类没有实现任何接口,使用CGLIB代理,否则使用JDK代理。
5、通过设置proxyTargetClass为true,可强制使用CGLIB代理(无论代理类是否实现接口)
6、如果目标类实现了一个(或者多个)接口,那么创建代理的类型将依赖ProxyFactoryBean的配置。(因为ProxyFactoryBean里有一个ProxyInterfaces属性,该属性可以查看该类实现了哪些接口)
7、如果ProxyFactoryBean的proxyInterfaces属性被设置为一个或者多个全限定接口名(包括包名、类名、完整的名称),基于JDK的代理将被创建。
8、如果ProxyFactoryBean的proxyInterfaces属性没有被设置,但是目标类实现了一个(或者更多)接口,那么ProxyFactoryBean将自动检测到这个目标类已经实现了至少一个接口,创建一个基于JDK的代理。
例子:创建基于接口的代理
ProxyFactoryBean工作原理:首先,定义了一个Id为personTarget的Bean,又定义了一个Id为myAdvisor的Bean,定义了一个Id为debugInterceptor的Bean,重点在接下来的定义,定义了一个Id为person的Bean,但是对应的并不是Person类,而是Spring提供的ProxyFactoryBean这个类,并且这个Bean配置里有一个属性名为proxyInterfaces的<property>配置,并且它的value值为Person的路径,另外一个<property>标签属性名称为targer并且指向personTarget(这个类是Person的具体实现类),当我们get()Id为person这个Bean时,返回的并不是ProxyFactoryBean的对象,而是ProxyFactoryBean里的getObject()返回的对象,它返回的是属性名为target指向的personTarget的对象,通过ProxyFactoryBean创建代理的时候,可以指定它的interceptor,interceptorNames属性,是一个集合,可以通过List、Value指定它的具体内容。
案例:(通过Spring API实现通知)
各种通知的类前面已经写好了,所以主要进行XML配置。
public interface BizLogic {
String save();
}
public class BizLogicImpl implements BizLogic {
@Override
public String save() {
System.out.println("BizLogicImpl: BizLogicImpl save");
return "BizLogicImpl save";
}
}
XML配置:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd"
default-autowire="constructor">
<bean id="afterReturningAdvice" class="springAPI.MoocAfterReturningAdvice" ></bean>
<bean id="beforeAdvice" class="springAPI.MoocBeforeAdvice" ></bean>
<bean id="methodInterceptor" class="springAPI.MoocMethodInterceptor" ></bean>
<bean id="throwsAdvice" class="springAPI.MoocThrowsAdvice" ></bean>
<bean id="pointcutBean" class="org.springframework.aop.support.NameMatchMethodPointcut">
<property name="mappedNames">
<list>
<value>sa*</value>
</list>
</property>
</bean>
<bean id="defaultAdvisor" class="org.springframework.aop.support.DefaultPointcutAdvisor">
<property name="advice" ref="beforeAdvice"></property>
<property name="pointcut" ref="pointcutBean"></property>
</bean>
<bean id="bizLogicImplTarget" class="springAPI.BizLogicImpl"></bean>
<bean id="bizLogicImpl" class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="target"><ref bean="bizLogicImplTarget"/></property>
<property name="interceptorNames">
<list>
<value>afterReturningAdvice</value>
<value>beforeAdvice</value>
<value>methodInterceptor</value>
<!-- <value>throwsAdvice</value>
-->
</list>
</property>
</bean>
</beans>
测试:
@Test
public void testApi(){
ApplicationContext ac=new ClassPathXmlApplicationContext("spring-ioc.xml");
BizLogic bl=(BizLogic)ac.getBean("bizLogicImpl");
bl.save();
}
结果:
前置通知的方法:save springAPI.BizLogicImpl
环绕前通知实现了
BizLogicImpl: BizLogicImpl save
环绕后通知实现了
返回后通知方法执行了save springAPI.BizLogicImpl null
查看全部 -
Spring的API实现AOP功能
1、这是Spring1.2的历史用法,现在Spring4.0仍然支持。
2、这是SpringAOP的基础
使用Spring的API实现AOP的原因:Schema-based(配置文件)和AspectJ实现AOP完全可以满足我们使用AOP的功能,使用API是因为这是SpringAOP的基础,可以加深我们应用和了解SpringAOP的理解(无论是注解方式、还是XML配置文件的方式,最终实现的基础都是和API密切关系的)。
3、现在SpringAOP的用法也是基于历史的,只是更简便了(在Spring1.2时配置文件很多,还需要了解各种配置)。
Spring的API
一、Pointcut(作为一个接口,有几个实现类)
1、Pointcut接口的实现类之一:NameMatchMethodPointcut,根据方法名字进行匹配。
2、该类有一个成员变量:mappedNames(它是一个集合,存放用于匹配的方法的名称),匹配方法的集合。
配置文件的例子:(sa开头的所有方法进行切入)
二、Before advice(原理:单独写一个前置通知的类,并实现MethodBeforeAdvice接口,并实现该接口的方法,)
1、一个简单的通知类型。
2、执行逻辑方法前被调用,不需要MethodInvocation对象。
3、前置通知可以在连接点执行之前插入自定义行为,但不能改变返回值。
案例:(会使用到后面的知识,暂时编写这些)
public class MoocBeforeAdvice implements MethodBeforeAdvice {
@Override
public void before(Method method, Object[] object, Object target) throws Throwable {
System.out.println("前置通知的方法:" + method.getName() + " " + target.getClass().getName());
}
}
三、Throws advice
1、如果连接点(执行的业务方法)出现异常,throws advice在方法返回后被调用。
2、如果throws-advice的方法抛出异常,那么它将覆盖原有异常。
3、接口org.springframework.aop.ThrowAdvice不包含任何方法,仅仅是一个声明,其实现类一定要实现类似这样的方法,void afterThrowing([Method, args, target],ThrowableSubclass);
Throws advice发生异常的说明
public void afterThrowing(Exception ex);//只有一个异常。
public void afterThrowing(RemoteException ex);//另外一种类型的异常。
public void afterThrowing(Method method,Object[] args(目标参数),Object target(对象),Exception ex);
public void afterThrowing(Method method,Object[] args(目标参数),Object target(对象),ServletException ex(其他类型的异常));
结论:异常通知(如上)方法参数里必须有异常,其他参数可有可无的。
抛出异常通知案例:
public class MoocThrowsAdvice implements ThrowsAdvice{
public void afterThrows(Exception ex){
System.out.println("抛出异常通知执行了");
}
public void afterThrows(Method method,Object[] object,Object target,Exception ex){
System.out.println("抛出异常通知执行了");
}
}
四、After Returning advice
1、返回后通知必须实现org.springframework.aop.AfterReturningAdvice接口(它和基于配置文件的配置方式是一样的)
2、可以访问返回值(但不能修改返回值)、被调用的方法、方法的参数和目标。
3、如果抛出异常,将会抛出拦截器链,替代返回值。
返回后通知案例
public class MoocAfterReturningAdvice implements AfterReturningAdvice {
@Override
public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
System.out.println("返回后通知方法执行了"+method.getName()+" "+target.getClass().getName()+" "+returnValue);
}
}
五、Interception around advice(环绕通知)
1、Spring的切入点模型使得切入点可以独立与advice重用,以针对不同的advice可以使用相同的切入点(和之前基于XML配置文件的AOP实现方式是一样的,切入点可以放在外面单独地去定义,通过Point reference,在每一个业务逻辑方法中都可以引用相同的切入点,当然,每个Advice也可以定义自己的Point cut)。
案例:
public class MoocMethodInterceptor implements MethodInterceptor {
@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
Object obj = null;
System.out.println("环绕前通知实现了");
obj = invocation.proceed();
System.out.println("环绕后通知实现了");
return null;
}
}
六、Introduction advice(和XML配置中的Introduction advice一样的功能)
1、Spring把引入通知作为一种特殊的拦截通知。
2、如果使用API实现AOP,则需要IntroductionAdvisor和IntroductionInterceptor这两个接口。
3、Introduction advice仅适用于类,不能和任何其它切入点一起使用。
如下为基于XML配置的Introduction advice内容
一个Spring test suite的例子
1、如果调用lock()方法,希望所有的setter方法抛出LockedException异常(使用场景:如使物体不可变,AOP典型例子)
2、需要完成繁重任务的IntroductionInterceptor,不是去实现该接口,而是使用org.springframework.aop.support.DelegatingIntroductionInterceptor
查看全部 -
Spring Bean装配之Bean的生命周期
1、Bean的定义:XML文件中的<bean>标签的配置。
2、Bean的初始化:IOC容器(ApplicationContext)加载配置文件,并生成Bean的实例。
3、Bean的使用:通过getBean()方法获得Bean的实例,并使用Bean。
4、Bean的销毁:在IOC容器销毁前,销毁所有由该容器创建的Bean的实例。
Bean的初始化和销毁(针对某一个Bean)
Bean的初始化(两种方式):
1、实现org.springframework.beans.factory.InitializingBean接口,覆盖afterPropertiesSet方法。
2、配置<bean>标签时,添加init-method属性。
Bean的销毁(两种方式)
1、实现org.springframework.beans.factory.DisposableBean接口,覆盖destroy方法(比如:释放一些连接池)。
2、配置<bean>标签的destroy-method属性。
Bean的初始化和销毁(针对所有Bean,在XML配置文件的描述中添加default-init-method="init",default-destroy-method="destroy",注意:默认的初始化配置属性,当Bean里没有该方法时,也不会报错)
注意:当同时使用Bean的属性配置、实现接口的配置,会先执行实现接口初始化和销毁方式、再执行配置初始化和销毁方式,默认属性配置不会生效,也就是说一个Bean没使用init-method属性和实现接口,那么这个默认属性配置则可以使用。
查看全部 -
Spring Bean装配之Bean的配置项及作用域
Bean的常用配置项(包括这些配置的含义和作用)
Id:整个IOC容器中Bean的唯一标识。
Class:具体实现的哪一个类,这里为类的路径(理论上只配置Class即可)。
Scope:Bean的作用域。
Constructor arguments:构造器的参数。
Properties:属性。
Autowiring mode:自动装配的模式。
lazy-initialization mode:懒加载模式。
Initialization/destruction method:初始化和销毁方法。
从IOC容器中获取对象的两种方式:
1、通过Id来获取,必须配置Id。
2、通过Bean的类型来获取,只配置Class即可。
Bean的作用域
singleton:单例,指一个IOC容器(ApplicationContext)中只存在一个标识singleton的对象(默认为单例模式)。
prototype:每次请求(通过getBean()方法)创建一个实例的时候,都会获得一个不同的对象,destroy方式不生效(请求完成后,这个实例也就不会再使用了,会被垃圾回收器回收)。
request:每次http请求创建一个实例,仅在当前的request内有效(request范围内,向IOC容器中获取这个对象,都是同一个实例,当有另一个的request请求,则会创建一个新的实例)。
session:同上,每次http请求创建,当前session内有效。
global session:基于portlet的web中有效(Spring对portlet也提供了支持),如果在单独的web应用中,golbal session和session作用域一样。
portlet定义了global session扩展:portlet更多做应用的集成,比如ON系统、HR系统、财务系统等等,通常在一个企业内部,不会每一个系统都去进行登录,所以就会使用porlet,把每一个常用的功能都列在里面,做成一个单点登录,点击相应任一区域或链接,跳到相应系统中,这其实就是两个系统两个Web应用了,所以这一定不会是一个session,所以global session就是指这种情况下session的作用范围。
Spring官网对作用域的解释
Bean的生命周期
Bean的自动装配
Resource和ResourceLoader(IOC加载资源文件(Spring的XML配置文件)用到的类,也可以加载其他资源文件)
查看全部 -
Spring Bean装配之Spring对JSR支持的说明
1:Spring支持使用JSR-250@Resource注解,通常用于成员变量和set方法上,这是一种在JavaEE5和6的通用模式(当然7也是支持的),Spring对对象的管理也支持这种方式。
2:@Resource有一个name属性,并且Spring默认把该name作为被注入bean的名称(如果没有显示指定name属性,默认名称是属性名或者set方法名,默认是根据注解的位置)。
3:注解提供的名字被解析为一个Bean的名称(@Resource的name属性),这个过程是由ApplicationContext中的CommonAnnotationBeanPostProcessor发现并处理的。
@PostConstruct和@PreDestroy
1:CommonAnnotationBeanPostProcessor不仅能识别JSR-250中的生命周期注解@Resource,在Spring2.5中引入支持初始化回调和销毁回调,前提是CommonAnnotationBeanPostProcessor这个类已经在Spring的ApplicationContext中注册了。(只有CommonAnnotationBeanPostProcessor类在IOC容器中注册了,才能处理@Resource,@PostConstruct,@PreDestroy)。
注意:自动注入,用Map<String,Object>map盛放所有的容器中的bean,然后循环打印所有的bean所属的类就能看到这个类了org.springframework.context.annotation.CommonAnnotationBeanPostProcessor,有这个类的bean。
@PostConstruct和@PreDestroy:这两个注解一般在的方法上,在初始化Bean和销毁之前,都会去调用这两个注解下的方法。
案例:(@Resource案例)
步骤1:
@Component
public class JsrDao {
public void print(){
System.out.println("JsrDao的方法执行了");
}
}
@Component
public class JsrService {
// @Resource
private JsrDao jsrDao;
public void print(){
jsrDao.print();
}
@Resource
public void setJsrDao(JsrDao jsrDao) {
this.jsrDao = jsrDao;
}
}
步骤2:
@Configuration
@ComponentScan
public class JsrConfig {
}
测试:
public class TestJsr {
@Test
public void testJsr(){
ApplicationContext ac=new AnnotationConfigApplicationContext(JsrConfig.class);
JsrService js=ac.getBean("jsrService",JsrService.class);
js.print();
}
}
结果:JsrDao的方法执行了
知识拓展:
@Resource和@Autowired都是做bean的注入时使用,其实@Resource并不是Spring的注解,它的包是javax.annotation.Resource,需要导入,但是Spring支持该注解的注入。
1、共同点
两者都可以写在字段和setter方法上。两者如果都写在字段上,那么就不需要再写setter方法。
2、不同点
(1)@Autowired
@Autowired为Spring提供的注解,需要导入包org.springframework.beans.factory.annotation.Autowired;只按照byType注入。
public class TestServiceImpl { // 下面两种@Autowired只要使用一种即可 @Autowired private UserDao userDao; // 用于字段上 @Autowired public void setUserDao(UserDao userDao) { // 用于属性的方法上 this.userDao = userDao; } }
@Autowired注解是按照类型(byType)装配依赖对象,默认情况下它要求依赖对象必须存在,如果允许null值,可以设置它的required属性为false。如果我们想使用按照名称(byName)来装配,可以结合@Qualifier注解一起使用。如下:
public class TestServiceImpl { @Autowired @Qualifier("userDao") private UserDao userDao; }
(2)@Resource
@Resource默认按照ByName自动注入,由J2EE提供,需要导入包javax.annotation.Resource。@Resource有两个重要的属性:name和type,而Spring将@Resource注解的name属性解析为bean的名字,而type属性则解析为bean的类型。所以,如果使用name属性,则使用byName的自动注入策略,而使用type属性时则使用byType自动注入策略。如果既不制定name也不制定type属性,这时将通过反射机制使用byName自动注入策略。
public class TestServiceImpl { // 下面两种@Resource只要使用一种即可 @Resource(name="userDao") private UserDao userDao; // 用于字段上 @Resource(name="userDao") public void setUserDao(UserDao userDao) { // 用于属性的setter方法上 this.userDao = userDao; } }
注:最好是将@Resource放在setter方法上,因为这样更符合面向对象的思想,通过set、get去操作属性,而不是直接去操作属性。
@Resource装配顺序:
①如果同时指定了name和type,则从Spring上下文中找到唯一匹配的bean进行装配,找不到则抛出异常。
②如果指定了name,则从上下文中查找名称(id)匹配的bean进行装配,找不到则抛出异常。
③如果指定了type,则从上下文中找到类似匹配的唯一bean进行装配,找不到或是找到多个,都会抛出异常。
④如果既没有指定name,又没有指定type,则自动按照byName方式进行装配;如果没有匹配,则回退为一个原始类型进行匹配,如果匹配则自动装配。
@Resource的作用相当于@Autowired,只不过@Autowired按照byType自动注入。
@PostConstruct和@PreDestroy(和以上案例相似,主要增加两个方法,并关闭Spring上下文)
@Component
public class JsrService {
@Resource
private JsrDao jsrDao;
public void print(){
jsrDao.print();
}
public void setJsrDao(JsrDao jsrDao) {
this.jsrDao = jsrDao;
}
@PostConstruct
public void jsrInit(){
System.out.println("Bean初始化方法执行了");
}
@PreDestroy
public void jsrDestroy(){
System.out.println("Bean销毁方法执行了");
}
}
测试:@Test
public void testJsr(){
AbstractApplicationContext ac=new AnnotationConfigApplicationContext(JsrConfig.class);
JsrService js=ac.getBean("jsrService",JsrService.class);
js.print();
ac.close();
}
结果:(和XML文件配置的init-method和destroy-method一样的功能)
Bean初始化方法执行了
JsrDao的方法执行了
Bean销毁方法执行了
使用JSR330标准注解
1:从Spring3.0开始支持JSR330标准注解(依赖注入注解),其扫描方式与Spring注解一致。
2:使用JSR330需要依赖javax.inject包。
3:使用Maven引入方式如下。
@Inject注解:等效于@Autowired,可以使用于类、属性、方法、构造器上。
@Named注解:使用特定名称进行依赖注入,与@Qualifier是等效的,还可以注解在类上,相当于@Component。(同一种类型的Bean在IOC容器中有多个时候,可以使用@Named指定特定的Bean)
在方法上:
在类上:
案例:@Inject和@Named
把@Componet换成@Named,@Resource换成Inject,执行结果相同。
查看全部
举报