AOP能够将那些与业务无关,却为业务模块所共同调用的逻辑和责任(例如事务管理,日志管理,权限管理等封装起来),便于减少系统的重复代码,降低模块间的耦合度,并有利于未来的可扩展性和可维护性。
spring AOP是基于动态代理的,如果要代理的对象实现了某个接口,那么springaop就会使用jdk动态代理去创建代理对象;而对于那些没有实现接口的对象,就无法使用jdk动态代理,转而使用cglib动态代理,生成一个被代理的对象的子类来作为管理。
在项目的开发中我们也可以使用aspectj,springAOP中已经集成了aspectj
通过自定义配置获取代理对象 进行调用方法
1. 目标类 = 接口+实现类
public interface UserService {
public void addUser();
}
public class UserServiceImpl implements UserService{
@Override
public void addUser() {
// TODO Auto-generated method stub
System.out.println("add User sofency");
}
public class InsertDemo{
public void before() {
System.out.println("插入前");
}
public void after() {
System.out.println("插入后");
}
}
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
//整合目标类和切面类
public class MyBeanFactory {
public static UserService createService() {
//目标类
final UserService userService = new UserServiceImpl();
//切面类
final InsertDemo insert = new InsertDemo();
//必须使用final修饰
/**
* 3.代理类 将目标类和切面类 结合
* Proxy.newProxyInstance
* 参数1: loader 类加载器 动态代理类 运行时创建 任何类都需要将其加载到内存
* 一般情况下 当前类.class.getClassLoader(); 也可以目标实例类
* 参数2:interfaces 代理类需要实现的所有接口
* 方式1:目标类实例.getClass().getInterfaces() 只能获得当前类中的接口 不能获得父元素的接口
* 参数3:InvocationHandler 处理类 接口 必须进行实现类 一般采用您名内部类
* 提供invoke()方法 代理类中的每个方法执行时 都将调用一次invoke()
* 参数31 Obejct 代理对象
* 参数32 Method 代理对象当前执行的方法的描述对象(反射)
* 过滤时 method.getName()
* 参数33 object[] args 方法实际参数
*/
UserService proxyUser = (UserService)Proxy.newProxyInstance(
MyBeanFactory.class.getClassLoader(),//也可以是目标类的类加载器
userService.getClass().getInterfaces(),//目标类里面的所有接口
new InvocationHandler() {
@Override
public Object invoke(Object arg0, Method method, Object[] arg2) throws Throwable {
insert.before();//前方法
Object object = method.invoke(userService);//生成代理对象
insert.after();//后置方法
return object;//返回代理对象赋值给proxyUser
}
});
return proxyUser;//返回代理对象
}
}
import org.junit.Test;
public class testDemo {
@Test
public void test() {
UserService userService = MyBeanFactory.createService();//获取代理对象
userService.addUser();//调用方法
}
}
cglib的设置和jdk动态代理的本质区别就是 jdk代理是通过接口的方式实现代理对象的生成
而cglib是通过继承的方式实现动态代理的生成。
public class UserServiceImpl {
public void addUser() {
System.out.println("cglib addUser");
}
public void deleteUser() {
System.out.println("cglib deleteUser");
}
}
public class MyAspect {
public void before() {
System.out.println("前方法");
}
public void after() {
System.out.println("后方法");
}
}
import java.lang.reflect.Method;
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
public class MyBeanFactory {
public static UserServiceImpl createProxy() {
//目标类
final UserServiceImpl user = new UserServiceImpl();
//切面类
final MyAspect myAspect = new MyAspect();
//核心类 增强
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(user.getClass());//设置将目标类设置为父类 让子类继承父类目标类)
//设置回调函数 MethodInterceptor 接口等效于jdk InvocationHandler接口
enhancer.setCallback(new MethodInterceptor() {
@Override
//intercep拦截的意思 将方法拦截后再进行增强
public Object intercept(Object arg0, Method method, Object[] args, MethodProxy arg3) throws Throwable {
//上面的参数和jdk动态代理一样
myAspect.before();//增强前方法
Object object = method.invoke(user, args);
myAspect.after();//增强后方法
return object;//将子类返回到enhance里面
}
});
UserServiceImpl userProxy = (UserServiceImpl) enhancer.create();//调用create方法创建(子类)代理类
return userProxy;
}
}
public class testDemo {
public static void main(String[] args) {
UserServiceImpl userService = MyBeanFactory.createProxy();
userService.addUser();
userService.deleteUser();
}
}
目标类=接口+实现类
和上面的一样就不再写了
切面类
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
/**
* 切面类确定通知 需要实现不同接口 接口就是规范 从而确定方法名称
* 采用环绕通知 MethodIntercpetor
* @author sofency
*
*/
public class MyAspect implements MethodInterceptor{
@Override
public Object invoke(MethodInvocation method) throws Throwable {
System.out.println("前方法");
//手动执行方法 method就是拦截到的方法
Object obj = method.proceed();
//切入点 即增强的方法 切面类和目标类整合到一起让spring去整合
System.out.println("后方法");
return null;
}
}
<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">
<bean id="UserServiceId" class="com.sofency.factory_bean.UserService">bean>
<bean id="userServiceImplId" class="com.sofency.factory_bean.UserServiceImpl">bean>
<bean id="myAspectId" class="com.sofency.factory_bean.MyAspect">bean>
<bean id="proxyServiceId" class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="interfaces" ref="UserServiceId">property>
<property name="target" ref="userServiceImplId">property>
<property name="interceptorNames" value="myAspectId">property>
<property name="optimize" value="true">property>
bean>
beans>
4.测试类
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class testDemo {
@Test
public void test() {
String xmlPath="com/sofency/factory_bean/beans.xml";
ApplicationContext applicationContext = new ClassPathXmlApplicationContext(xmlPath);
UserService user = (UserService)applicationContext.getBean("proxyServiceId");
user.addUser();
}
}
需要依赖的jar包
spring-aop-4.1.3.RELEASE.jar
com.springsource.org.aopalliance-1.0.0.jar
com.springsource.org.aspectj.weaver-1.6.8.RELEASE.jar
1.目标类省略 和前面的一样
2. 切面类的配置
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
public class MyAspectjXML {
//前置通知
public void before(JoinPoint joinPoint) {
System.out.println("前置方法"+joinPoint.getSignature().getName());
}
//后置通知
public void after(Object object) {//有参数的情况
// System.out.println("后置方法"+joinPoint.getSignature().getName());
System.out.println("后置方法"+object);
}
//环绕通知 必须是ProceedJoinPoint 类型 try
public Object MyAround(ProceedingJoinPoint joinPoint) throws Throwable{
System.out.println("前置通知");
Object obj = joinPoint.proceed();//将返回值赋给Object
System.out.println("后置通知"+obj);
return obj;
}
//抛出异常通知 catch
public void MyThrowable(JoinPoint joinPoint,Throwable e) {
System.out.println("捕获到异常");
System.out.println(e.getMessage());
}
//最终通知finally
public void MyFinally() {
System.out.println("最终通知");
}
}
3.xml的配置
<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="UserServiceId" class="com.sofency.AspectjXML.UserServiceImpl">bean>
<bean id="MyAspectId" class="com.sofency.AspectjXML.MyAspectjXML">bean>
<aop:config>
<aop:aspect ref="MyAspectId" >
<aop:pointcut expression="execution(* com.sofency.AspectjXML.UserServiceImpl.*(..))" id="MyPointCut"/>
<aop:around method="MyAround" pointcut-ref="MyPointCut">aop:around>
<aop:after-throwing method="MyThrowable" pointcut-ref="MyPointCut" throwing="e"/>
<aop:after method="MyFinally" pointcut-ref="MyPointCut"/>
aop:aspect>
aop:config>
beans>
注意 前置通知是before
后置通知是after-returning
还有抛出异常通知 after-throwing
最终通知是after
类似于try catch finally
环绕通知 可以替代掉前置通知和后置通知
4.测试类(测试的时候将上面xml中的五种通知一个一个测试 )
public class testAspectjXML {
@Test
public void testDemo() {
String xmlPath="com/sofency/AspectjXML/beans.xml";
ApplicationContext applicationContext = new ClassPathXmlApplicationContext(xmlPath);
UserService userService = (UserService) applicationContext.getBean("UserServiceId");
userService.addUser();
userService.deleteUser();
}
}