有问题可以问我,spring前面的几篇文章不清不楚,我整体过一遍再重新写
AOP联盟通知类型
advice
- 回忆一下什么是advice?
- 要是不记得的话可以看看我
spring-03-1
里面例子的那个图片,说白了,advice里面就是指向的增强功能的代码内容AOP联盟为通知Advice定义了org.aopalliance.aop.Advice这个类,且Spring按照通知Advice在目标类方法的连接点位置,可以分为5类:
- 前置通知 org.springframework.aop.MethodBeforeAdvice
- 在目标方法执行前实施增强
- 后置通知 org.springframework.aop.AfterReturningAdvice
- 在目标方法执行后实施增强
- 环绕通知 org.springframework.aop.MethodIntercepter
- 在目标方法执行前后实施增强
- 异常抛出通知 org.springframework.aop.ThrowsAdvice
- 在方法抛出异常后实施增强
- 引介通知 org.springframework.aop.IntroductionInterceptor
- 在目标类中添加一些新的方法和属性
spring AOP采用半自动代理
什么是代理半自动呢?
- 代理半自动是指,我们不用手动去创建代理对象,我们直接在xml中配置代理对象,然后从容器中获取就好了
配置代理的注意事项:
必须有两个属性,一个切面类对象,一个目标类对象,接口有就写没有就不写.
半自动代理默认使用的是jdk的动态代理方式,有接口一定是jdk的动态代理,没有接口就使用cglib的代理方式.
可以通过在配置文件中强制使用cglib增强代理,在spring配置的bean中添加这样一行: ,这样,就会只使用cglib增强代理了
当你想使用jdk的动态代理的时候,那就写上接口,并且不要强制使用cglib增强代理,在源码中,接口和切面类(准确的说是增强代码所在的类的对象)那个属性的定义其实是一个list集合,但是只有一个的时候我们可以直接写的value,多个接口的时候就要写成集合了
案例:
第一步: 导入jar包(idea创建spring项目都会自动导入不用管):
四个核心包(beans,context,core,expression)
一个日志包(logging)
AOP联盟包(定义了规范,里面都是接口,com.springsource.org.aopalliance.jar)
spring-aop包(aop联盟的实现,spring-aop-3.2.0.RELEASE.jar)
第二步: 创建目标类
public class UserService {
public void addUser() {
System.out.println("添加了一个用户!");
}
}
第三步: 创建切面类
public class MyAspect implements MethodInterceptor {
@Override
public Object invoke(MethodInvocation methodInvocation) throws Throwable {
//拦截方法
System.out.println("开启事务");
//放行
Object obj = methodInvocation.proceed();
System.out.println("结束事务");
System.out.println("已成功拦截,切入点前开启事务,切入点后提交事务");
return obj;
}
}
第四步: spring配置
<?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">
<!--配置userservice-->
<bean id="userService" class="UserService">
<property name="name" value="樊磊"></property>
</bean>
<!--配置一个切面类对象-->
<bean id="myAspect" class="MyAspect">
</bean>
<!--配置代理,有接口就采用jdk动态代理,没有就采用cglib代理,默认使用jdk动态代理-->
<bean id="serviceProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
<!--接口-->
<!--<property name="interfaces" value="接口全路径"></property>-->
<!--目标对象-->
<property name="target" ref="userService"></property>
<!--切面类对象 注意这里是value,不是ref,和目标对象区分一下子-->
<property name="interceptorNames" value="myAspect"></property>
</bean>
</beans>
第五步: 测试类
public class Test {
public static void main(String[] args) {
//获取spring容器中的代理对象
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
UserService userService = (UserService) context.getBean("serviceProxy");
//调用方法
//此处的userService为代理对象
userService.addUser();
}
}
spring AOP采用全自动代理
这个就是说,我们本来是需要通过代理实现目标类获取功能增强,但是现在直接让增强的功能和目标类相连接,直接跳过代理这个东西,但是切面类这些的还是要自己整哈.
什么是全自动织入
结合我
spring-03-1
那篇博客里的图,织入(weaving)就是将advice通过代理类添加到目标类中的过程.全自动织入,emm,可以理解为我们相对于半自动代理来说不需要配置代理类,直接让切面里的通知送到切入点进行结合.
结合案例说明xml配置中expression表达式的写法
**<aop:config proxy-target-class=“true”>**这表示使用的是cglib代理,值为false或者不写表示使用的是jdk的代理(基于接口的时候使用).
execution中参数的意义:以
execution(* example1.service.*.*(..))
为例.
括号里
第一个*
,表示任意返回值,即不管方法的返回值是int还是String或者其它类型都会被过滤,也可以指定为int或者其他类型.括号中的
example1.service
表示包名,表示切入点在这个包下,同时注意这里的包名是src下面的报名,即如果是src.package1.package2.targetClass
的话,就写成package1.package2
.括号里的
第二个*
,也就是example1.service.*
的这个*号表示该包下面的所有类都会被过滤,被当作切入点,也可以指定类名,例如:
example1.service.fan
,这就表示只有该包下的fan
的这个类会被过滤和增强.
example1.service..fan
,就只是多了一个.
,这表示service这个包及其子包下的fan这个类都会被过滤
- 括号里的
第三个*
,也就是example1.service.*.*
中的第二个*,它表示类下面的所有方法都会被过滤,被当作切入点,也可以指定某个方法或者以某个单词开头的方法作为切入点,例如:
example1.service.fan.test
表示只有fan这个类下面的test()方法会被过滤.
example1.service.fan.user*
表示fan这个类下面所有以user开头的方法都会被过滤
example1.service..fan.user*
表示service及其子包下面的fan这个类下面的user开头的方法都会被过滤
- 括号中的
(..)
,表示方法的参数为任意个数,任意类型参数都会被过滤,也可以自己指定,例如:
()
什么都不写表示只过滤空参的方法.
(*)
表示只过滤一个参数的方法,参数可以为任意类型
(*,*)
表示只过滤两个参数的方法,多个参数用,
隔开即可
(*,int)
表示只过滤两个参数且第二个参数为int类型的方法,多个参数用,
隔开即可案例:
第一步:
导入jar包: aspectjweaverjar.版本尽量高一点吧
第二步:
切面类: 注意此时一定要继承接口而且我们这里继承的是aop联盟的接口不是cglib的接口
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
public class MyAspect implements MethodInterceptor {
@Override
public Object invoke(MethodInvocation methodInvocation) throws Throwable {
System.out.println("开启事务");
//放行
Object obj = methodInvocation.proceed();
System.out.println("结束事务");
return obj;
}
}
第三步:
目标类
public class UserService {
public void addUser() {
System.out.println("添加了一个用户!");
}
}
第四步:
beans.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"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<!--配置目标类对象-->
<bean id="user" class="example1.service.User"></bean>
<!--配置切面类对象-->
<bean id="myAspect" class="example1.MyAspect"></bean>
<!--配置aop
1. 在bean中配置aop约束
2. 配置aop:config,把切入点和通知相结合-->
<aop:config proxy-target-class="true">
<!--切入点
expression-表达式
要求每个service的方法前后分别开启事务和关闭事务-->
<aop:pointcut id="myPointCut" expression="execution(* example1.service.*.*(..))"></aop:pointcut>
<!--通知 要关联切入点-->
<aop:advisor advice-ref="myAspect" pointcut-ref="myPointCut"></aop:advisor>
</aop:config>
</beans>
第五步:
测试类:
public class UnitClass {
@Test
public void test() {
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
User user = (User) context.getBean("user");
user.addUser();
}
}