学习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种 通知的简单例子就分享到这里啦。
来源:oschina
链接:https://my.oschina.net/u/1444899/blog/217739