2020-03-16 Spring-Aop
简介
AOP:面向切面编程,是OOP的扩展和延申,解决OOP中遇到的问题
可以进行权限校验,日志记录,性能监控,事务校验
Spring底层的AOP实现原理:动态代理
JDK动态代理 :只能对实现了接口的类产生代理
Cglib动态代理(类似于Javassist第三方代理技术):对没有实现接口的类产生代理对象,生成子
对象
两种动态代理
JDK动态代理实例
1.建立一个接口:
public interface UserDao {
public void insert();
public void update();
}
2.接口实现类
public class UserDaoImpl implements UserDao {
@Override
public void insert() {
System.out.println("insert方法");
}
@Override
public void update() {
System.out.println("update方法");
}
}
3.编写代理类JdkProxy
public class JdkProxy{
//将被增强的对象传递到代理当中
private UserDao userDao;
public JdkProxy(UserDao userDao) {
this.userDao = userDao;
}
public UserDao creatProxy() {
UserDao userDaoProxy = (UserDao) Proxy.newProxyInstance(userDao.getClass().getClassLoader(), userDao.getClass().getInterfaces(), this);
return userDaoProxy;
}
}
- 实现InvocationHandler接口,并重写其中方法,用于权限管理
public class JdkProxy implements InvocationHandler{
······
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if("insert".equals(method.getName())) {
System.out.println("权限增强");
return method.invoke(userDao, args);
}
return method.invoke(userDao, args);
}
5.测试类
@Test
public void demo1() {
UserDao userDao=new UserDaoImpl();
//创建代理
UserDao proxy = new JdkProxy(userDao).creatProxy();
proxy.insert();
proxy.update();
}
Cglib动态代理
第三方开源代码生成类库,动态添加类的属性和方法
1.编写接口和测试类(同上)
2.编写代理类CglibProxy
public class CglibProxy {
private UserDaoImpl userDaoImpl;
public CglibProxy(UserDaoImpl userDaoImpl) {
this.userDaoImpl=userDaoImpl;
}
public UserDaoImpl createProxy() {
//1.创建Cglib的代理对象
Enhancer enhancer = new Enhancer();
//2.设置父类
enhancer.setSuperclass(userDaoImpl.getClass());
//3.设置回调
enhancer.setCallback(this);
//4.创建代理
UserDaoImpl proxy=(UserDaoImpl) enhancer.create();
return proxy;
}
3.CglibProxy 继承MethodInterceptor类,并重写其中方法
public class CglibProxy implements MethodInterceptor{
······
@Override
public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
// 判断是否是insert方法
if("insert".equals(method.getName())) {
//增强
System.out.println("权限校验");
return methodProxy.invokeSuper(proxy, args);
}
return methodProxy.invokeSuper(proxy, args);
}
}
4.测试类
@Test
public void demo1() {
UserDaoImpl userDao=new UserDaoImpl();
//创建代理
UserDaoImpl proxy = new CglibProxy(userDao).createProxy();
proxy.insert();
proxy.update();
}
Spring的AOP开发
简介
AOP思想最早是由AOP联盟组织提出的,Spring是使用这种思想最好的框架
Spring的AOP有自己实现的方式(非常繁琐)AspectJ是一个AOP框架,Spring引入了AspectJ作为自身开发
相关代码编写
1.新建切面类,并在切面类中MethodInterceptor类(环绕通知)
//环绕通知
public class MyAspectXML implements MethodInterceptor{
@Override
public Object invoke(MethodInvocation arg0) throws Throwable {
System.out.println("0000000000");
Object obj=arg0.proceed();
System.out.println("0000000000");
return obj;
}
}
2.在xml文件中配置
<!-- 配置目标对象,被增强的对象-->
<bean id="productDao" class="com.zut.aopTest.ProductDaoImpl"></bean>
<!-- 将切面类交给Spring管理 -->
<bean id="myAspect" class="com.zut.aopTest.MyAspectXML"></bean>
3.在xml文件中配置通知
<!-- 创建代理对象 默认JDK动态代理 如果没有proxyInterfaces 则默认cglib-->
<bean class="org.springframework.aop.framework.ProxyFactoryBean" name="proxyBean">
<property name="targetName" value="productDao"></property>
<!-- 拦截器名字 -->
<property name="interceptorNames" value="myAspect">
<!-- 如果通知在多个类中 -->
<!-- <array>
<value></value>
</array> -->
</property>
<property name="proxyInterfaces" value="com.zut.aopTest.ProductDao"></property>
</bean>
4.编写测试类
public void demo1() {
ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
ProductDao productDao=context.getBean("proxyBean",ProductDao.class);
productDao.save();
}
前置通知接口:MethodBrforeAdvice
后置通知接口:AfterReturningAdvice 注意: 有异常时后置通知不执行
异常通知接口:ThrowsAdvice
最终通知接口:AfterAdvice
在此方式下,xml文件可以采用自动织入编写
1.xml文件
<!-- 配置目标对象,被增强的对象 -->
<bean id="productDao" class="com.zut.aopTest.ProductDaoImpl"></bean>
<!-- 将切面类交给Spring管理 -->
<bean id="myAspect" class="com.zut.aopTest.MyAspectXML"></bean>
<aop:config>
<aop:pointcut expression="execution(* com.zut.springAOP.UserImpl.*(..))" id="pointcut"/>
<aop:advisor advice-ref="myAspect" pointcut-ref="pointcut"/>
</aop:config>
//id 与 pointcut-ref 相等
2.测试类
public void demo1() {
ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
ProductDao productDao=context.getBean("productDao",ProductDao.class);
productDao.save();
}
采用AspectJ的XML的方式
相关配置文件
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>5.2.2.RELEASE</version>
</dependency>
<dependency>
<groupId>aopalliance</groupId>
<artifactId>aopalliance</artifactId>
<version>1.0</version>
</dependency>
<dependency>
<groupId>aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.5.3</version>
</dependency>
xml文件
clipboard.png相关代码编写
1.编写接口和测试类
2.编写切面类
public class MyAspectXML {
public void checkPri() {
System.out.println("权限校验。。。。。。。。。");
}
}
3.在xml文件里进行配置
<!-- 配置目标对象,被增强的对象 -->
<bean id="productDao" class="com.aopTest.ProductDaoImpl"></bean>
<!-- 将切面类交给Spring管理 -->
<bean id="myAspect" class="com.aopTest.MyAspectXML"></bean>
<!-- 通过AOP的配置实现对目标产生代理 -->
<aop:config>
<!-- 表达式配置那些类哪些方法需要进行增强 ()里面只写参数类型 可以写成 save(int ,String) and args(a,b) -->
<aop:pointcut expression="execution(* com.aopTest.ProductDaoImpl.save(..))" id="pointcut1"/>
<!-- 配置切面 myAspect指向切面 与上文要对应上 checkPri 切面类里的方法名-->
<aop:aspect ref="myAspect">
<!--匹配参数 这里的名字要一致-->
<aop:before method="checkPri" pointcut-ref="pointcut1" arg-names="a"/></aop:aspect>
</aop:config>
</beans>
4.测试类
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:Spring-config.xml")
public class SpringDemo1 {
@Resource(name="productDao")
private ProductDao productDao;
@Test
public void demo1() {
productDao.save();
productDao.update();
productDao.find();
productDao.delete();
}
}
通知类型
前置通知
在目标方法执行之前执行此操作
1.xml文件
<!-- 配置切面 -->
<aop:aspect ref="myAspect">
<aop:before method="checkPri" pointcut-ref="pointcut1"/>
</aop:aspect>
2.获得切入点信息(几种通知均可实现)
在切入类中
public void checkPri(JoinPoint joinPoint) {
System.out.println("权限校验。。。。。。。。。"+joinPoint);
}
后置通知
在目标方法之后执行的操作
1.在xml文件中
<aop:pointcut expression="execution(* com.aopTest.ProductDaoImpl.delete(..))" id="pointcut2"/>
<!-- 后置通知 -->
<aop:after-returning method="write" pointcut-ref="pointcut2" returning="result"/>
//returning表示获取返回值
2.获得方法返回值
在切入类中
/**
* 后置通知
*/
public void write(Object result) {
System.out.println("日志通知。。。。。。。。。。"+result);
}
//这里的reult必须与前面配置里的returning一致
环绕通知
在目标方法执行之前和之后进行操作
1.xml文件
<aop:pointcut expression="execution(* com.aopTest.ProductDaoImpl.update(..))" id="pointcut3"/>
<!-- 环绕通知 -->
<aop:around method="around" pointcut-ref="pointcut3"/>
2.在切入类
/**
* 环绕通知
* 性能监控
* @throws Throwable
*/
public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("环绕通知前。。。");
Object obj=joinPoint.proceed();
System.out.println("环绕通知后。。。");
return obj;
}
异常抛出通知
在程序出现异常时,进行的操作
1.xml文件中
<aop:pointcut expression="execution(* com.aopTest.ProductDaoImpl.find(..))" id="pointcut4"/>
<!-- 异常通知 -->
<aop:after-throwing method="afterThrowing" pointcut-ref="pointcut4" throwing="ex"/>
//throwing:得到异常类型
2.切入类
/**
* 异常抛出
*/
public void afterThrowing(Throwable ex) {
System.out.println("异常抛出。。。"+ex);
}
//ex必须与上文配置中的throwing一致
最终通知
无论代码是否有异常,总是会执行,相当于finally代码块
1.xml文件里
<!-- 最终通知 -->
<aop:after method="after" pointcut-ref="pointcut4"/>
2.切面类
/**
* 最终通知
*/
public void after() {
System.out.println("最终通知。。。...");
}
spring的切入点表达式写法
基于execution的函数完成的
语法:
[访问修饰符] 方法返回值 包名.类名.方法名(参数)
public void com.zut.aopTest.ProductDao.save(..)
星号(*)代表任意,参数不能写 * ,可以写 ..
*****Dao.save(..)
*com.zut.aopTest.ProductDao+save(..)