2019独角兽企业重金招聘Python工程师标准>>>
学习Spring AOP 之前,先要了解下JAVA的动态代理。如果不清楚动态代理的概念就百度一下吧。废话不多说,直接上代码。
我们模拟一个简单的登录
首先我们创建一个用户登录的接口
package com.proxy.test;public interface UserLogin {public void login(String userName);
}
接着创建一个接口的实现类
package com.proxy.test;public class UserLoginImpl implements UserLogin {public void login(String userName) {System.out.println("欢迎 " + userName + " 登录系统");}
}
在创建一个自己的处理类 实现InvocationHandler 接口
package com.proxy.test;import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;public class MyHandler implements InvocationHandler {private Object obj;public MyHandler(Object obj) {this.obj = obj;}public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {beforeLogin(); // 登录前处理,更具自己需要来写Object result = method.invoke(obj, args); // 调用真正的方法afterLogin(); // 登录后处理,更具自己需要来写return result;}public void beforeLogin() {System.out.println("登录前处理");}public void afterLogin() {System.out.println("登录后处理");}
}
最后写一个测试类
package com.proxy.test;import java.lang.reflect.Proxy;public class ProxyTest {public static void main(String[] args) {UserLoginImpl user = new UserLoginImpl(); // 得到实例对象MyHandler handler = new MyHandler(user); // 将对象传入自己的处理器中UserLogin proxy = (UserLogin) Proxy.newProxyInstance(user.getClass().getClassLoader(), user.getClass().getInterfaces(), handler); // 得到代理对象proxy.login("张三"); // 代理调用login方法}
}
运行结果 输出 :
登录前处理
欢迎 张三 登录系统
登录后处理
说明我们的动态代理成功了。以上就是一个动态代理的小例子。
下面说说spring AOP 中间的4种通知(前置,后置,环绕,异常),别的理论和一些东西说一下也说不明白,自己也还需要继续学习和理解所以就不在这里献丑了。
前置通知
首先我们还是先建立一个用户登录的接口
package com.aop.test;public interface UserLogin {public void login(String userName);
}
然后用一个类去实现这个接口(前置,后置,环绕这3个同时都用同一个类,异常的稍微修改下,让它能抛出异常)
package com.aop.test;public class UserLoginImpl implements UserLogin {public void login(String userName) {System.out.println(userName + " 正在登录系统");}
}
创建实现前置通知接口 MethodBeforeAdvice 的类
package com.aop.test;import java.lang.reflect.Method;import org.springframework.aop.MethodBeforeAdvice;public class CheckUser implements MethodBeforeAdvice {public void before(Method method, Object[] objs, Object obj) throws Throwable {String userName = (String) objs[0]; // 获取登录名System.out.println("用户 " + userName + " 登录前处理");}}
测试类JAVA代码版
package com.aop.test;import org.springframework.aop.BeforeAdvice;
import org.springframework.aop.framework.ProxyFactory;public class BeforeAdviceTest {public static void main(String[] args) {UserLogin target = new UserLoginImpl(); // 具体的登录用户BeforeAdvice advice = new CheckUser(); // 前置通知ProxyFactory proxyFactory = new ProxyFactory(); // Spring代理工厂proxyFactory.setTarget(target); // 设置代理目标proxyFactory.addAdvice(advice); // 为代理目标添加前置通知UserLogin proxy = (UserLogin) proxyFactory.getProxy(); // 生成代理实例proxy.login("张三"); // 调用登录方法}
}
但是一般都是用xml文件来配置的所以在来个xml配置版
建立一个bean1.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN"
"http://www.springframework.org.dtd/spring-beans.dtd">
<beans><bean id="checkuser" class="com.aop.test.CheckUser"/><bean id="target" class="com.aop.test.UserLoginImpl"/><!-- 使用Spring代理工厂配置一个代理 --><bean id="userlogin" class="org.springframework.aop.framework.ProxyFactoryBean"><!-- 指定代理接口,如果是多个接口,可以使用List元素指定 --><property name="proxyInterfaces" value="com.aop.test.UserLogin"/><!-- 指定通知 --><property name="interceptorNames" value="checkuser"/><!-- 指定目标对象 --> <!-- 这个地方的name 我开始按照书上写的target报错,我进ProxyFactoryBean类看 没有target属性和set它的方法,有targetName属性修改了就对了--><property name="targetName" value="target"/></bean>
</beans>
测试类变为
package com.aop.test;import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;public class BeforeAdviceTest1 {public static void main(String[] args) {ApplicationContext ac = new ClassPathXmlApplicationContext("/com/aop/test/bean1.xml");UserLogin ul = (UserLogin) ac.getBean("userlogin");ul.login("张三");}
}
运行结果在控制台输出:
用户 张三 登录前处理
张三 正在登录系统
后置通知
创建实现后置通知接口的实现类
package com.aop.test;import java.lang.reflect.Method;import org.springframework.aop.AfterReturningAdvice;public class AfterLoginAdvice implements AfterReturningAdvice {public void afterReturning(Object paramObject1, Method paramMethod, Object[] paramArrayOfObject, Object paramObject2)throws Throwable {String userName = (String) paramArrayOfObject[0]; // 获取登录名System.out.println(userName + " 登录成功");}}
后置通知 只需要修改以下配置的xml文件
为了方便我就新建立一个bean2.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN"
"http://www.springframework.org.dtd/spring-beans.dtd">
<beans><bean id="target" class="com.aop.test.UserLoginImpl"/><bean id="afterlogin" class="com.aop.test.AfterLoginAdvice"/><!-- 使用Spring代理工厂配置一个代理 --><bean id="userlogin" class="org.springframework.aop.framework.ProxyFactoryBean"><!-- 指定代理接口,如果是多个接口,可以使用List元素指定 --><property name="proxyInterfaces" value="com.aop.test.UserLogin"/><!-- 指定通知 --><property name="interceptorNames" value="afterlogin"/><!-- 指定目标对象 --> <!-- 这个地方的name 我开始按照书上写的target报错,我进ProxyFactoryBean类看 没有target属性和set它的方法,有targetName属性修改了就对了--><property name="targetName" value="target"/></bean>
</beans>
测试类
package com.aop.test;import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;public class BeforeAdviceTest2 {public static void main(String[] args) {ApplicationContext ac = new ClassPathXmlApplicationContext("/com/aop/test/bean2.xml");UserLogin ul = (UserLogin) ac.getBean("userlogin");ul.login("张三");}
}
控制台打印出:
张三 正在登录系统
张三 登录成功
环绕通知
创建实现环绕通知的类
package com.aop.test;import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;public class MyAroundAdvice implements MethodInterceptor {public Object invoke(MethodInvocation invocation) throws Throwable {Object[] objs = invocation.getArguments();String userName = (String) objs[0];// 在目标方法执行前调用System.out.println("正在对" + userName + "进行登录验证");// 通过反射调用执行方法Object obj = invocation.proceed();// 在目标方法执行之后调用System.out.println(userName + "登录成功");return obj;}}
是不是感觉这个类和动态代理的处理类很像呢?
建立bean3.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN"
"http://www.springframework.org.dtd/spring-beans.dtd">
<beans><bean id="myaroundadvice" class="com.aop.test.MyAroundAdvice"/><bean id="target" class="com.aop.test.UserLoginImpl"/><!-- 使用Spring代理工厂配置一个代理 --><bean id="userlogin" class="org.springframework.aop.framework.ProxyFactoryBean"><!-- 指定代理接口,如果是多个接口,可以使用List元素指定 --><property name="proxyInterfaces" value="com.aop.test.UserLogin"/><!-- 指定通知 --><property name="interceptorNames" value="myaroundadvice"/><!-- 指定目标对象 --> <!-- 这个地方的name 我开始按照书上写的target报错,我进ProxyFactoryBean类看 没有target属性和set它的方法,有targetName属性修改了就对了--><property name="targetName" value="target"/></bean>
</beans>
测试类
package com.aop.test;import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;public class BeforeAdviceTest3 {public static void main(String[] args) {ApplicationContext ac = new ClassPathXmlApplicationContext("/com/aop/test/bean3.xml");UserLogin ul = (UserLogin) ac.getBean("userlogin");ul.login("张三");}
}
控制台输出:
正在对张三进行登录验证
张三 正在登录系统
张三登录成功
异常通知
我们修改下UserLoginImpl类
package com.aop.test;public class UserLoginImpl implements UserLogin {public void login(String userName) {if ("张三".equals(userName)) {System.out.println(userName + " 正在登录系统");} else {throw new RuntimeException("用户名不正确");}}
}
创建实现异常通知的类
package com.aop.test;import java.lang.reflect.Method;import org.springframework.aop.ThrowsAdvice;public class ExceptionAdvice implements ThrowsAdvice {public void afterThrowing(Method method, Object[] objs, Object target, Exception ex) {System.out.println("Method:" + method.getName() + "抛出异常: " + ex.getMessage());}
}
创建bean4.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN"
"http://www.springframework.org.dtd/spring-beans.dtd">
<beans><bean id="exceptionadvice" class="com.aop.test.ExceptionAdvice"/><bean id="target" class="com.aop.test.UserLoginImpl"/><!-- 使用Spring代理工厂配置一个代理 --><bean id="userlogin" class="org.springframework.aop.framework.ProxyFactoryBean"><!-- 指定代理接口,如果是多个接口,可以使用List元素指定 --><property name="proxyInterfaces" value="com.aop.test.UserLogin"/><!-- 指定通知 --><property name="interceptorNames" value="exceptionadvice"/><!-- 指定目标对象 --> <!-- 这个地方的name 我开始按照书上写的target报错,我进ProxyFactoryBean类看 没有target属性和set它的方法,有targetName属性修改了就对了--><property name="targetName" value="target"/></bean>
</beans>
测试类
package com.aop.test;import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;public class BeforeAdviceTest4 {public static void main(String[] args) {ApplicationContext ac = new ClassPathXmlApplicationContext("/com/aop/test/bean4.xml");UserLogin ul = (UserLogin) ac.getBean("userlogin");ul.login("张ss");}
}
输入非 张三 就打印出
Method:login抛出异常: 用户名不正确 并且抛出 java.lang.RuntimeException: 用户名不正确
输入 张三就 打印出
张三 正在登录系统
java动态代理,和spring aop的4种 通知的简单例子就分享到这里啦。