JAVA动态代理 和 Spring AOP 4种通知的简单实现
来源:互联网 发布:淘宝经营类目的优势 编辑:程序博客网 时间:2024/06/05 14:35
学习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种通知的简单实现
- JAVA动态代理 和 Spring AOP 4种通知的简单实现
- Spring AOP 代理实现的两种方式: JDK动态代理 和 Cglib框架动态代理
- Spring中AOP的两种代理方式(Java动态代理和CGLIB代理)
- Spring中AOP的两种代理方式(Java动态代理和CGLIB代理)
- Spring中AOP的两种代理方式(Java动态代理和CGLIB代理)
- Spring中AOP的两种代理方式(Java动态代理和CGLIB代理)
- Spring中AOP的两种代理方式(Java动态代理和CGLIB代理)
- Spring中AOP的两种代理方式(Java动态代理和CGLIB代理)
- Spring中AOP的两种代理方式(Java动态代理和CGLIB代理)
- Spring中AOP的两种代理方式(Java动态代理和CGLIB代理)
- Spring中AOP的两种代理方式(Java动态代理和CGLIB代理)
- 10077---Spring中AOP的两种代理方式(Java动态代理和CGLIB代理)
- Spring AOP java动态代理实现
- spring对AOP的支持(JDK的动态代理实现AOP和CGLIB实现AOP)
- 使用Java动态代理实现简单AOP
- JAVA动态代理实现aop的简单例子
- java spring aop 的代理的简单实现
- zookeeperd无法启动的分析
- Flask url_for如何生成动态的endpoint
- Python decorator 2: 类
- 遇到Class path contains multiple SLF4J bindings.该如何解决?
- 棋盘覆盖
- JAVA动态代理 和 Spring AOP 4种通知的简单实现
- nodejs中的中间件--Multer
- 欢迎使用CSDN-markdown编辑器---临时测试
- Error:Android Packager: [应用名] java.io.FileNotFoundException: G:\xxx\应用名.unaligned.apk (拒绝访问。)
- 一些球形包围盒,AABB包围盒,OBB包围盒的文章
- sort函数
- 网站会员整合ucenter 实现多站点同步登录方法
- 单链表实现约瑟夫环(JosephCircle)(C语言)
- Topcoder SRM 413 (Div 2) 1000.InfiniteSequence