- 浏览: 255466 次
- 性别:
- 来自: 广州
-
文章分类
最新评论
-
pshaoyi:
连接创建没问题,在执行的时候就报错了。Caused by: c ...
jdbc连接池简单封装 -
imbangbang:
中文乱码,没有解决掉
Java实现Zip压缩与解压(解决中文乱码问题) -
chjy1983:
麻烦也发份给我,chl_82@126.com,谢谢!
Java实现Zip压缩与解压(解决中文乱码问题) -
jidacwj:
楼主写的这个有问题呀首先,多线程分割文件第45行,我尝试打印每 ...
Java 大文件读取排序 -
www6wzk:
非常好的学习例子,十分感谢!
Jbpm工作流实例—报销申请
AOP是Aspect Oriented Programming的缩写,意思是面向方面编程,一种新兴的编程技术。
AOP实际是GoF设计模式的延续,设计模式孜孜不倦追求的是调用者和被调用者之间的解耦,
AOP可以说也是这种目标的一种实现。它可以解决OOP和过程化方法不能够很好解决的横切
(crosscut)问题,
如:事务、安全、日志等横切关注。当未来系统变得越来越复杂,
横切关注点就成为一个大问题的时候,AOP就可以很轻松的解决横切关注点这个问题。
比如有这样一个情景:
这个例子虽然是很好的面向对象代码,但是在业务处理逻辑中夹杂这日志处理和权限判断,变得复杂混乱.
在 AOP 中,正交关注点(如安全和日志记录)被识别为系统中的常见横切关注点。说它们是横切,
是因为它们总是切入模块(如包、类和代码文件)的多个单位。也许横切关注点可能不是核心业务逻辑的一部分,但是它们是应用程序的基本部分。
AOP的实现主要是通过方法的拦截实现.在不使用AOP框架的情况下,我们可以通过JDK提供的动态代理来实现方法的拦截
注意:使用JDK提供的动态代理实现
要求我们的目标对象必须实现接口
IUserBean接口
IUserBean实现类 UserBean.java
我们希望在UserBean执行方法之前先检查userName是不是为空,以此做为权限判断.
当然我们可以在没个方法里面去加这些判断,但是这需要为每个方法都添加同样的判断,维护不便.
使用JDK提供的动态代理技术可以很方便的实现上面的需求:
通过java.lang.reflect.Proxy;提供的
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
方法可以生成一个动态代理对象
其中
loader是类装载器
interfaces是目标对象实现的一系列接口
h是一个实现InvocationHandler接口的类,我们对代理对象的所有操作都经过它处理
这样我们就可以拦截到UserBean的方法,在方法执行前先判断是否有权限,如果有则执行方法,
没有权限的话就不执行方法.
编写我们的代理类:
JDKProxy.java
通过调用createProxyObject可以生成代理对象,
编写测试类如下:
执行成功后输出:
this is addUser() method!
再次修改测试类:
即当用户没有权限时,控制台不输出东西,说明我们拦截方法对其做的权限判断生效了.
从上面这个例子可以成功拦截了调用的方法并对其做了相应的处理
如果不使用JDK提供的Proxy类
通过cglib创建代理类,好处是不要求我们的目标对象实现接口
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(this.targetObject.getClass());
enhancer.setCallback(this); //回调,参数是一个实现MethodInterceptor接口的类,我们对代理对象的所有操作都经过它处理
return enhancer.create(); //创建代理对象
修改UserBean 去掉IUserBean接口
通过cglib创建代理类
CGLibProxy.java
编写测试类:
输出:
this is addUser() method!
当取消用户权限时,控制台不输出任何东西.
说明通过CGLib成功生成代理对象,拦截了目标对象的方法.
Spring主要通过代理来实现AOP
下面是AOP的一些基本概念:
切面(Aspect):对横切关注点的抽象(类似类对对象的抽象)
连接点(JoinPoint):被拦截到的点,泛指方法
切入点(CutPoint):对哪些连接点进行拦截的定义
通知(Advice):在特定的连接点,AOP框架执行的动作.前置/后置/例外/最终/环绕通知(调用方法之前执行,全部执行完毕之后)
引入(Introduction): 添加方法或字段到被通知的类。 Spring允许引入新的接口到任何被通知的对象。例如,你可以使用一个引入使任何对象实现 IsModified接口,来简化缓存。
目标对象(Target Object): 包含连接点的对象。也被称作 被通知或被代理对象。
AOP代理(AOP Proxy): AOP框架创建的对象,包含通知。 在Spring中,AOP代理可以是JDK动态代理或者CGLIB代理。
织入(Weaving): 组装方面来创建一个被通知对象。这可以在编译时 完成(例如使用AspectJ编译器),也可以在运行时完成。Spring和其他纯Java AOP框架一样, 在运行时完成织入。
Adive通知可理解如下:
Spring提供两种方式实现AOP
一种是XML配置的方式
一种是annotation注解的方式
不管采用哪种方式,都必须在spring的配置文件中配置AOP支持:
其中<aop:aspectj-autoproxy/>表示打开aspect注解处理器
(aspect的内容具体可查看http://www.ibm.com/developerworks/cn/java/j-aspectj/)
使用注解方式实现AOP必须引入三个jar包:
aspectjweaver.jar
aspectjrt.jar
cglib.jar
首先建立好测试用的业务bean
然后我们需要定义一个切面/定义切入点/通知等
接口IUserBean.java
实现类UserBean.java
紧接着我们建立我们的切面类:使用@Aspect注解
MyInterceptor.java
切面定义好之后我们必须交给Spring管理,配置我们的bean.xml文件如下:
编写测试类如下:
运行测试类输出:
前置通知
进入方法
this is addUser() method!
后置通知
最终通知
退出方法
可以看出定义的各个通知的执行顺序,
例外通知只有在程序异常的情况下才会发生.
其他通知都会执行.
我们也可以在环绕通知里面将前面的几个通知实现了.
如果需要获取方法的参数我们必须在定义通知的时候做响应的设置:
比如我在前置通知希望获取到输入的参数需要修改MyInterceptor如下:
重新运行测试类输出:
前置通知royzhou
进入方法
this is addUser() method!
后置通知
最终通知
退出方法
可见我们成功的获取到了方法的参数
如果需要获取方法的返回值,则修改如下:
输出结果是:
前置通知1
进入方法
this is getUser() method!
后置通知haha
最终通知
退出方法
可见方法的返回值我们也成功拿到了.
如需在例外通知中获取例外的详细信息,我们只需要配置:
上面的例子介绍了使用注解方式来实现Spring的AOP
另外我们可以使用XML来配置:
使用XML配置Spring的AOP 我们的切面类MyInterceptor不需要做任何注解,就是一个普通的Java类
接下来我们需要在bean.xml文件中配置我们的切面/切入点/通知等信息
注意在前置通知中不支持args-name指定参数,必须在pointcut中指定,否则服务器抛出异常:0 formal unbound in pointcut
总结一下AOP的优点:
面对方法编程并不是要取代面对对象编程,而是要提高它。AOP程序员一般来说都是90%使用OOP来解决问题,而10%是用AOP来解决OOP不能解决的问题。
横切关注点(Cross-cutting Concerns)
很多时候你发现你的类并不能十分清晰和明白的表到你所想表达的功能意义,因为你真正的代码
大多被其它代码所包围了。如果你想很好的扩展或集成你所想表达的功能意义,你最好就用方面
的思想来考虑它了。
开发中的分层(Layering Based on Deployment)
AOP另外一个很有用的地方就是可以用来为你的应用程序分层。很多时候你希望的一些特殊应用或
类是可以很好的配置的,但同时也希望这些东西是不臃肿和可以扩展的。AOP提供了很好的途径来
分层这些复杂的东西。JBOSS AOP提供了XML配置的机制来配置每个方面的开发。最好的例子就是
缓存服务,它提供了不同的锁机制。这些缓存锁机制可以很好的织入你的类,而不影响你的类的
代码,这样你的类就是很好的扩展性了。
透明性(Transparency)
很多时候你都想把你的程序的焦点集中在商务应用和应用逻辑上,而不是关注于中间件的开发。
AOP允许你透明的应用中间件而不再使你的代码收到污染。一个很好的例子就是JBOSS AOP中的
用户认证上面。
异常处理
处理异常是AOP提供给我们另外一个很有用的东西。例如,SQLException异常包含了SQL语句的
异常信息或者数据库的死锁等信息,但这些信息却使用不同错误代码和信息。AOP可以让你拦截
SQL语句信息,并分类处理数据库死锁信息。
已修改 呵呵
只是总结一下 方便以后查阅
AOP实际是GoF设计模式的延续,设计模式孜孜不倦追求的是调用者和被调用者之间的解耦,
AOP可以说也是这种目标的一种实现。它可以解决OOP和过程化方法不能够很好解决的横切
(crosscut)问题,
如:事务、安全、日志等横切关注。当未来系统变得越来越复杂,
横切关注点就成为一个大问题的时候,AOP就可以很轻松的解决横切关注点这个问题。
比如有这样一个情景:
public class AccountManager { private static final sysLogger = SystemLogger.getInstance(); private AuthorizationManager authMgr = new AuthorizationManager(); public void transferFunds(String from, String to, int amount) { sysLogger.log("transfer funds from " + from + " to " + to); if(authMgr.accessAble(from) && authMgr.accessAble(to)) { sysLogger.log("access successfully"); CustomerAccount from = findAccount(from); CustomerAccount to = findAccount(to); from.debit(amount); to.credit(amount); } else { sysLogger.log("access deny"); } sysLogger.log("transfer funds from " + from + " to " + to + " $" + amount + " successfully!"); } }
这个例子虽然是很好的面向对象代码,但是在业务处理逻辑中夹杂这日志处理和权限判断,变得复杂混乱.
在 AOP 中,正交关注点(如安全和日志记录)被识别为系统中的常见横切关注点。说它们是横切,
是因为它们总是切入模块(如包、类和代码文件)的多个单位。也许横切关注点可能不是核心业务逻辑的一部分,但是它们是应用程序的基本部分。
AOP的实现主要是通过方法的拦截实现.在不使用AOP框架的情况下,我们可以通过JDK提供的动态代理来实现方法的拦截
注意:使用JDK提供的动态代理实现
要求我们的目标对象必须实现接口
IUserBean接口
package com.royzhou.aop; public interface IUserBean { public void getUser(); public void addUser(); public void updateUser(); public void deleteUser(); }
IUserBean实现类 UserBean.java
package com.royzhou.aop; public class UserBean implements IUserBean { private String user = null; public UserBean() { } public UserBean(String user) { this.user = user; } public void setUser(String user) { this.user = user; } public void addUser() { System.out.println("this is addUser() method!"); } public void deleteUser() { System.out.println("this is deleteUser() method!"); } public void getUser() { System.out.println("this is getUser() method!"); } public void updateUser() { System.out.println("this is updateUser() method!"); } }
我们希望在UserBean执行方法之前先检查userName是不是为空,以此做为权限判断.
当然我们可以在没个方法里面去加这些判断,但是这需要为每个方法都添加同样的判断,维护不便.
使用JDK提供的动态代理技术可以很方便的实现上面的需求:
通过java.lang.reflect.Proxy;提供的
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
方法可以生成一个动态代理对象
其中
loader是类装载器
interfaces是目标对象实现的一系列接口
h是一个实现InvocationHandler接口的类,我们对代理对象的所有操作都经过它处理
这样我们就可以拦截到UserBean的方法,在方法执行前先判断是否有权限,如果有则执行方法,
没有权限的话就不执行方法.
编写我们的代理类:
JDKProxy.java
package com.royzhou.aop; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; public class JDKProxy implements InvocationHandler { private Object targetObject; public Object createProxyObject(Object targetObject) { this.targetObject = targetObject; //生成代理对象 return Proxy.newProxyInstance(this.targetObject.getClass().getClassLoader(),this.targetObject.getClass().getInterfaces(),this); } public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { UserBean userBean = (UserBean) targetObject; String userName = userBean.getUserName(); Object result = null; //权限判断 if(userName!=null && !"".equals(userName)) { //调用目标对象的方法 result = method.invoke(targetObject, args); } return result; } }
通过调用createProxyObject可以生成代理对象,
编写测试类如下:
package com.royzhou.aop; public class TestProxy { public static void main(String[] args) { JDKProxy jProxy = new JDKProxy(); IUserBean userBean = (IUserBean) jProxy.createProxyObject(new UserBean("royzhou")); userBean.addUser(); } }
执行成功后输出:
this is addUser() method!
再次修改测试类:
package com.royzhou.aop; public class TestProxy { public static void main(String[] args) { JDKProxy jProxy = new JDKProxy(); IUserBean userBean = (IUserBean) jProxy.createProxyObject(new UserBean()); userBean.addUser(); } }
即当用户没有权限时,控制台不输出东西,说明我们拦截方法对其做的权限判断生效了.
从上面这个例子可以成功拦截了调用的方法并对其做了相应的处理
如果不使用JDK提供的Proxy类
通过cglib创建代理类,好处是不要求我们的目标对象实现接口
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(this.targetObject.getClass());
enhancer.setCallback(this); //回调,参数是一个实现MethodInterceptor接口的类,我们对代理对象的所有操作都经过它处理
return enhancer.create(); //创建代理对象
修改UserBean 去掉IUserBean接口
package com.royzhou.aop; public class UserBean { private String userName = null; public UserBean() { } public UserBean(String userName) { this.userName = userName; } public void addUser() { System.out.println("this is addUser() method!"); } public void deleteUser() { System.out.println("this is deleteUser() method!"); } public void getUser() { System.out.println("this is getUser() method!"); } public void updateUser() { System.out.println("this is updateUser() method!"); } public String getUserName() { return userName; } public void setUserName(String userName) { this.userName = userName; } }
通过cglib创建代理类
CGLibProxy.java
package com.royzhou.aop; import java.lang.reflect.Method; import net.sf.cglib.proxy.Enhancer; import net.sf.cglib.proxy.MethodInterceptor; import net.sf.cglib.proxy.MethodProxy; public class CGLibProxy implements MethodInterceptor { private Object targetObject; public Object createProxyObject(Object targetObject) { this.targetObject = targetObject; Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(this.targetObject.getClass()); //非final 进行覆盖 enhancer.setCallback(this); //回调,通过 return enhancer.create(); //创建代理对象 } public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable { UserBean userBean = (UserBean) targetObject; String userName = userBean.getUserName(); Object result = null; if(userName!=null && !"".equals(userName)) { //调用目标对象的方法 result = methodProxy.invoke(targetObject, args); } return result; } }
编写测试类:
package com.royzhou.aop; public class TestProxy { public static void main(String[] args) { CGLibProxy cProxy = new CGLibProxy(); UserBean userBean = (UserBean) cProxy.createProxyObject(new UserBean("royzhou")); userBean.addUser(); } }
输出:
this is addUser() method!
当取消用户权限时,控制台不输出任何东西.
说明通过CGLib成功生成代理对象,拦截了目标对象的方法.
Spring主要通过代理来实现AOP
下面是AOP的一些基本概念:
切面(Aspect):对横切关注点的抽象(类似类对对象的抽象)
连接点(JoinPoint):被拦截到的点,泛指方法
切入点(CutPoint):对哪些连接点进行拦截的定义
通知(Advice):在特定的连接点,AOP框架执行的动作.前置/后置/例外/最终/环绕通知(调用方法之前执行,全部执行完毕之后)
引入(Introduction): 添加方法或字段到被通知的类。 Spring允许引入新的接口到任何被通知的对象。例如,你可以使用一个引入使任何对象实现 IsModified接口,来简化缓存。
目标对象(Target Object): 包含连接点的对象。也被称作 被通知或被代理对象。
AOP代理(AOP Proxy): AOP框架创建的对象,包含通知。 在Spring中,AOP代理可以是JDK动态代理或者CGLIB代理。
织入(Weaving): 组装方面来创建一个被通知对象。这可以在编译时 完成(例如使用AspectJ编译器),也可以在运行时完成。Spring和其他纯Java AOP框架一样, 在运行时完成织入。
Adive通知可理解如下:
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { UserBean userBean = (UserBean) targetObject; String userName = userBean.getUserName(); Object result = null; if(userName!=null && !"".equals(userName)) { //调用目标对象的方法 try { //前置通知 result = method.invoke(targetObject, args); //后置通知 } catch(Exception e) { //例外通知 } finally { //最终通知 } } //环绕通知(前置通知之后,目标对象方法调用之前执行,全部执行完毕(最终通知)之后) return result; }
Spring提供两种方式实现AOP
一种是XML配置的方式
一种是annotation注解的方式
不管采用哪种方式,都必须在spring的配置文件中配置AOP支持:
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd"> <aop:aspectj-autoproxy/> </beans>
其中<aop:aspectj-autoproxy/>表示打开aspect注解处理器
(aspect的内容具体可查看http://www.ibm.com/developerworks/cn/java/j-aspectj/)
使用注解方式实现AOP必须引入三个jar包:
aspectjweaver.jar
aspectjrt.jar
cglib.jar
首先建立好测试用的业务bean
然后我们需要定义一个切面/定义切入点/通知等
接口IUserBean.java
package com.royzhou.aop; public interface IUserBean { public void getUser(); public void addUser(); public void updateUser(); public void deleteUser(); }
实现类UserBean.java
package com.royzhou.aop; public class UserBean { public void addUser(String userName) { System.out.println("this is addUser() method!"); } public void deleteUser(int userId) { System.out.println("this is deleteUser() method!"); } public String getUser(String userId) { System.out.println("this is getUser() method!"); return "haha"; } public void updateUser(int userId, String userName) { System.out.println("this is updateUser() method!"); } }
紧接着我们建立我们的切面类:使用@Aspect注解
MyInterceptor.java
package com.royzhou.aop; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.After; import org.aspectj.lang.annotation.AfterReturning; import org.aspectj.lang.annotation.AfterThrowing; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import org.aspectj.lang.annotation.Pointcut; @Aspect public class MyInterceptor { /** * 定义切入点 * 第一个*表示方法的返回值,这里使用通配符,只有返回值符合条件的才拦截,(!void表示有返回值) * 第一个..表示com.royzhou.aop包及其子包 * 倒数第二个*表示包下的所有Java类都被拦截 * 最后一个*表示类的所有方法都被拦截 * (..)表示方法的参数可以任意多个如[(java.lang.String,java.lang.Integer)表示第一个参数是String,第二个参数是int的方法才会被拦截] */ @Pointcut("execution(* com.royzhou.aop..*.*(..))") //定义一个切入点,名称为pointCutMethod(),拦截类的所有方法 private void pointCutMethod() { } @Before("pointCutMethod()") //定义前置通知 public void doBefore() { System.out.println("前置通知"); } @AfterReturning("pointCutMethod()") //定义后置通知 public void doAfterReturning() { System.out.println("后置通知"); } @AfterThrowing("pointCutMethod()") //定义例外通知 public void doAfterException() { System.out.println("异常通知"); } @After("pointCutMethod()") //定义最终通知 public void doAfter() { System.out.println("最终通知"); } @Around("pointCutMethod()") //定义环绕通知 public Object doAround(ProceedingJoinPoint pjp) throws Throwable { System.out.println("进入方法"); Object object = pjp.proceed(); //必须执行pjp.proceed()方法,如果不执行此方法,业务bean的方法以及后续通知都不执行 System.out.println("退出方法"); return object; } }
切面定义好之后我们必须交给Spring管理,配置我们的bean.xml文件如下:
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd"> <aop:aspectj-autoproxy/> <bean id="MyInterceptor" class="com.royzhou.aop.MyInterceptor" /> <bean id="UserBean" class="com.royzhou.aop.UserBean"></bean> </beans>
编写测试类如下:
package com.royzhou.aop; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class TestAOP { public static void main(String[] args) { ApplicationContext ctx = new ClassPathXmlApplicationContext("bean.xml"); UserBean ub = (UserBean)ctx.getBean("UserBean"); ub.addUser("royzhou"); } }
运行测试类输出:
前置通知
进入方法
this is addUser() method!
后置通知
最终通知
退出方法
可以看出定义的各个通知的执行顺序,
例外通知只有在程序异常的情况下才会发生.
其他通知都会执行.
我们也可以在环绕通知里面将前面的几个通知实现了.
如果需要获取方法的参数我们必须在定义通知的时候做响应的设置:
比如我在前置通知希望获取到输入的参数需要修改MyInterceptor如下:
package com.royzhou.aop; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.After; import org.aspectj.lang.annotation.AfterReturning; import org.aspectj.lang.annotation.AfterThrowing; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import org.aspectj.lang.annotation.Pointcut; @Aspect public class MyInterceptor { /** * 定义切入点 * 第一个*表示方法的返回值,这里使用通配符,只有返回值符合条件的才拦截 * 第一个..表示com.royzhou.aop包及其子包 * 倒数第二个*表示包下的所有Java类 * 最后一个*表示类的所有方法 * (..)表示方法的参数可以任意多个 */ @Pointcut("execution(* com.royzhou.aop..*.*(..))") //定义一个切入点,名称为pointCutMethod(),拦截类的所有方法 private void pointCutMethod() { } //需要两个条件同时成立. args(userName)代表只有一个参数且为String类型 名称必须与doBefore方法的参数名称一样 @Before("pointCutMethod() && args(userName)") //定义前置通知 public void doBefore(String userName) { System.out.println("前置通知" + userName); } @AfterReturning("pointCutMethod()") //定义后置通知 public void doAfterReturning() { System.out.println("后置通知"); } @AfterThrowing("pointCutMethod()") //定义例外通知 public void doAfterException() { System.out.println("异常通知"); } @After("pointCutMethod()") //定义最终通知 public void doAfter() { System.out.println("最终通知"); } @Around("pointCutMethod()") //定义环绕通知 public Object doAround(ProceedingJoinPoint pjp) throws Throwable { System.out.println("进入方法"); Object object = pjp.proceed(); //必须执行pjp.proceed()方法,如果不执行此方法,业务bean的方法以及后续通知都不执行 System.out.println("退出方法"); return object; } }
重新运行测试类输出:
前置通知royzhou
进入方法
this is addUser() method!
后置通知
最终通知
退出方法
可见我们成功的获取到了方法的参数
如果需要获取方法的返回值,则修改如下:
package com.royzhou.aop; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.After; import org.aspectj.lang.annotation.AfterReturning; import org.aspectj.lang.annotation.AfterThrowing; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import org.aspectj.lang.annotation.Pointcut; @Aspect public class MyInterceptor { /** * 定义切入点 * 第一个*表示方法的返回值,这里使用通配符,只有返回值符合条件的才拦截 * 第一个..表示com.royzhou.aop包及其子包 * 倒数第二个*表示包下的所有Java类 * 最后一个*表示类的所有方法 * (..)表示方法的参数可以任意多个 */ @Pointcut("execution(* com.royzhou.aop..*.*(..))") //定义一个切入点,名称为pointCutMethod(),拦截类的所有方法 private void pointCutMethod() { } //需要两个条件同时成立. args(userName)代表只有一个参数且为String类型 名称必须与doBefore方法的参数名称一样 @Before("pointCutMethod() && args(userName)") //定义前置通知 public void doBefore(String userName) { System.out.println("前置通知" + userName); } //配置returning="result", result必须和doAfterReturning的参数一致 @AfterReturning(pointcut="pointCutMethod()", returning="result") //定义后置通知 public void doAfterReturning(String result) { System.out.println("后置通知" + result); } @AfterThrowing("pointCutMethod()") //定义例外通知 public void doAfterReturning() { System.out.println("异常通知"); } @After("pointCutMethod()") //定义最终通知 public void doAfter() { System.out.println("最终通知"); } @Around("pointCutMethod()") //定义环绕通知 public Object doAround(ProceedingJoinPoint pjp) throws Throwable { System.out.println("进入方法"); Object object = pjp.proceed(); //必须执行pjp.proceed()方法,如果不执行此方法,业务bean的方法以及后续通知都不执行 System.out.println("退出方法"); return object; } }
输出结果是:
前置通知1
进入方法
this is getUser() method!
后置通知haha
最终通知
退出方法
可见方法的返回值我们也成功拿到了.
如需在例外通知中获取例外的详细信息,我们只需要配置:
package com.royzhou.aop; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.After; import org.aspectj.lang.annotation.AfterReturning; import org.aspectj.lang.annotation.AfterThrowing; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import org.aspectj.lang.annotation.Pointcut; @Aspect public class MyInterceptor { /** * 定义切入点 * 第一个*表示方法的返回值,这里使用通配符,只有返回值符合条件的才拦截 * 第一个..表示com.royzhou.aop包及其子包 * 倒数第二个*表示包下的所有Java类 * 最后一个*表示类的所有方法 * (..)表示方法的参数可以任意多个 */ @Pointcut("execution(* com.royzhou.aop..*.*(..))") //定义一个切入点,名称为pointCutMethod(),拦截类的所有方法 private void pointCutMethod() { } //需要两个条件同时成立. args(userName)代表只有一个参数且为String类型 名称必须与doBefore方法的参数名称一样 @Before("pointCutMethod() && args(userName)") //定义前置通知 public void doBefore(String userName) { System.out.println("前置通知" + userName); } //配置returning="result", result必须和doAfterReturning的参数一致 @AfterReturning(pointcut="pointCutMethod()", returning="result") //定义后置通知 public void doAfterReturning(String result) { System.out.println("后置通知" + result); } //类似returning的配置 @AfterThrowing(pointcut="pointCutMethod()", throwing="e") //定义例外通知 public void doAfterException(Exception e) { System.out.println("异常通知"); } @After("pointCutMethod()") //定义最终通知 public void doAfter() { System.out.println("最终通知"); } @Around("pointCutMethod()") //定义环绕通知 public Object doAround(ProceedingJoinPoint pjp) throws Throwable { System.out.println("进入方法"); Object object = pjp.proceed(); //必须执行pjp.proceed()方法,如果不执行此方法,业务bean的方法以及后续通知都不执行 System.out.println("退出方法"); return object; } }
上面的例子介绍了使用注解方式来实现Spring的AOP
另外我们可以使用XML来配置:
使用XML配置Spring的AOP 我们的切面类MyInterceptor不需要做任何注解,就是一个普通的Java类
package com.royzhou.aop; import org.aspectj.lang.ProceedingJoinPoint; public class MyInterceptor { public void doBefore(String userName) { System.out.println("前置通知" + userName); } public void doAfterReturning(String result) { System.out.println("后置通知" + result); } public void doAfterException(Exception e) { System.out.println("异常通知"); } public void doAfter() { System.out.println("最终通知"); } public Object doAround(ProceedingJoinPoint pjp) throws Throwable { System.out.println("进入方法"); Object object = pjp.proceed(); //必须执行pjp.proceed()方法,如果不执行此方法,业务bean的方法以及后续通知都不执行 System.out.println("退出方法"); return object; } }
接下来我们需要在bean.xml文件中配置我们的切面/切入点/通知等信息
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd"> <aop:aspectj-autoproxy/> <bean id="aspjectbean" class="com.royzhou.aop.MyInterceptor" /> <bean id="UserBean" class="com.royzhou.aop.UserBean"></bean> <aop:config> <aop:aspect id="asp" ref="aspjectbean"> <aop:pointcut id="mycut" expression="execution(* com.royzhou.aop..*.*(..))"/> <aop:before pointcut="execution(* com.royzhou.aop..*.*(..)) and args(userName)" method="doBefore" /> <aop:after-returning pointcut-ref="mycut" method="doAfterReturning" returning="result" /> <aop:after-throwing pointcut-ref="mycut" method="doAfterReturning" throwing="e" /> <aop:after pointcut-ref="mycut" method="doAfter"/> <aop:around pointcut-ref="mycut" method="doArround"/> </aop:aspect> </aop:config> </beans>
注意在前置通知中不支持args-name指定参数,必须在pointcut中指定,否则服务器抛出异常:0 formal unbound in pointcut
总结一下AOP的优点:
面对方法编程并不是要取代面对对象编程,而是要提高它。AOP程序员一般来说都是90%使用OOP来解决问题,而10%是用AOP来解决OOP不能解决的问题。
横切关注点(Cross-cutting Concerns)
很多时候你发现你的类并不能十分清晰和明白的表到你所想表达的功能意义,因为你真正的代码
大多被其它代码所包围了。如果你想很好的扩展或集成你所想表达的功能意义,你最好就用方面
的思想来考虑它了。
开发中的分层(Layering Based on Deployment)
AOP另外一个很有用的地方就是可以用来为你的应用程序分层。很多时候你希望的一些特殊应用或
类是可以很好的配置的,但同时也希望这些东西是不臃肿和可以扩展的。AOP提供了很好的途径来
分层这些复杂的东西。JBOSS AOP提供了XML配置的机制来配置每个方面的开发。最好的例子就是
缓存服务,它提供了不同的锁机制。这些缓存锁机制可以很好的织入你的类,而不影响你的类的
代码,这样你的类就是很好的扩展性了。
透明性(Transparency)
很多时候你都想把你的程序的焦点集中在商务应用和应用逻辑上,而不是关注于中间件的开发。
AOP允许你透明的应用中间件而不再使你的代码收到污染。一个很好的例子就是JBOSS AOP中的
用户认证上面。
异常处理
处理异常是AOP提供给我们另外一个很有用的东西。例如,SQLException异常包含了SQL语句的
异常信息或者数据库的死锁等信息,但这些信息却使用不同错误代码和信息。AOP可以让你拦截
SQL语句信息,并分类处理数据库死锁信息。
评论
18 楼
hzxlb910
2013-07-17
要提供代码下载就好了,我就喜欢拷贝粘贴,呵呵,谢谢你的文章
17 楼
ciki
2010-08-03
不错,收藏,学习中。
16 楼
kililanxilu
2010-06-24
用Aspectj里面的测试方法中,应该是IUserBean ub = (IUserBean)ctx.getBean("UserBean"); 吧?
15 楼
kililanxilu
2010-06-18
不错的帖子,写的很详细~!
14 楼
zuozhengfeng
2010-03-31
总结得挺好的!支持啊
13 楼
gowish
2010-03-05
不错,正好用到,对AOP有了个大致了解。
12 楼
ronartest
2010-01-06
汗,,为什么不顺便传一下代码
11 楼
ariestiger
2009-11-24
好东西,讲得蛮清楚的。
个人感觉啊,讲AOP的家伙都在前面要讲一大堆的
Joinpoint:它定义在哪里加入你的逻辑功能,对于Spring AOP,Jointpoint指的就是Method。
Advice:特定的Jointpoint处运行的代码,对于Spring AOP 来讲,有Before advice、AfterreturningAdvice、ThrowAdvice、AroundAdvice(MethodInteceptor)等。
Pointcut:一组Joinpoint,就是说一个Advice可能在多个地方织入,
Aspect:实际是Advice和Pointcut的组合,但是Spring AOP 中的Advisor也是这样一个东西,但是Spring中为什么叫Advisor而不叫做Aspect。
Target:被通知的对象。
Proxy:将通知应用到目标对象后创建的对象
Weaving:将Aspect加入到程序代码的过程,对于Spring AOP,由ProxyFactory或者ProxyFactoryBean负责织入动作。
这种玩意,我当初就是被这些鬼东西搞糊了,
其实我觉得如果是用AOP的话,用标注比配置文件要好,为什么?你哪来那么多需求要通过配置文件来修改什么切面,连接点?我觉得是完全没必要的,能用标注就标注。
其实说穿了,在Spring中:
一个切面就是一个类,这个类中有些方法会在其他对象的某些方法执行前,执行后,执行中触发执行。。。。。
一个切入点就是一个空方法,它就是一个点,我的理解是只是为了把业务方法(也就是那些将要被切面切到的方法)刺一个洞出来,好让切面中的方法能织入进去。。。。
一个Advice就是一个切面方法了,不过分了几种,什么之前(before),之后(after),等乱七八糟的,它就是切面要织入业务方法的方法(有点别扭,但我的理解就是这样的,把两个不相干的方法揉在一起去)。。。。。。。
还有一个问题,我怎么知道,你这些切面方法要切入哪些类,哪些方法?这个就是在切入点中的那个表达式来搞定的,由这个表达式来匹配,匹配的类当中的方法将被织入。。。。。
就这么多,感觉,用的时候我想不应该弄得那么复杂,尽量保持清晰,简单,那些太过复杂的玩意尽管能显示个人功力,但我觉得本来就是为了所谓的解耦,本来就是为了什么代码的清晰,搞得太复杂反而不好。。。。。。。
个人感觉啊,讲AOP的家伙都在前面要讲一大堆的
Joinpoint:它定义在哪里加入你的逻辑功能,对于Spring AOP,Jointpoint指的就是Method。
Advice:特定的Jointpoint处运行的代码,对于Spring AOP 来讲,有Before advice、AfterreturningAdvice、ThrowAdvice、AroundAdvice(MethodInteceptor)等。
Pointcut:一组Joinpoint,就是说一个Advice可能在多个地方织入,
Aspect:实际是Advice和Pointcut的组合,但是Spring AOP 中的Advisor也是这样一个东西,但是Spring中为什么叫Advisor而不叫做Aspect。
Target:被通知的对象。
Proxy:将通知应用到目标对象后创建的对象
Weaving:将Aspect加入到程序代码的过程,对于Spring AOP,由ProxyFactory或者ProxyFactoryBean负责织入动作。
这种玩意,我当初就是被这些鬼东西搞糊了,
其实我觉得如果是用AOP的话,用标注比配置文件要好,为什么?你哪来那么多需求要通过配置文件来修改什么切面,连接点?我觉得是完全没必要的,能用标注就标注。
其实说穿了,在Spring中:
一个切面就是一个类,这个类中有些方法会在其他对象的某些方法执行前,执行后,执行中触发执行。。。。。
一个切入点就是一个空方法,它就是一个点,我的理解是只是为了把业务方法(也就是那些将要被切面切到的方法)刺一个洞出来,好让切面中的方法能织入进去。。。。
一个Advice就是一个切面方法了,不过分了几种,什么之前(before),之后(after),等乱七八糟的,它就是切面要织入业务方法的方法(有点别扭,但我的理解就是这样的,把两个不相干的方法揉在一起去)。。。。。。。
还有一个问题,我怎么知道,你这些切面方法要切入哪些类,哪些方法?这个就是在切入点中的那个表达式来搞定的,由这个表达式来匹配,匹配的类当中的方法将被织入。。。。。
就这么多,感觉,用的时候我想不应该弄得那么复杂,尽量保持清晰,简单,那些太过复杂的玩意尽管能显示个人功力,但我觉得本来就是为了所谓的解耦,本来就是为了什么代码的清晰,搞得太复杂反而不好。。。。。。。
10 楼
310628570
2009-11-12
挺好的~支持一下 呵呵~
9 楼
pan_java
2009-10-23
不错,不知那些人评了新手贴!
8 楼
royzhou1985
2009-10-08
pan_java 写道
不对吧没有变量类型
private static final sysLogger = SystemLogger.getInstance();
参数与变量同名
public void transferFunds(String from, String to, int amount)
CustomerAccount from = findAccount(fromAcct);
CustomerAccount to = findAccount(toAcct);
private static final sysLogger = SystemLogger.getInstance();
参数与变量同名
public void transferFunds(String from, String to, int amount)
CustomerAccount from = findAccount(fromAcct);
CustomerAccount to = findAccount(toAcct);
已修改 呵呵
7 楼
royzhou1985
2009-10-08
whaosoft 写道
lz 其实这已经是好多地方用了很久了 不过也支持你
只是总结一下 方便以后查阅
6 楼
acmilancn
2009-10-08
看完对AOP有个大概的了解了
5 楼
whaosoft
2009-10-08
lz 其实这已经是好多地方用了很久了 不过也支持你
4 楼
pan_java
2009-10-08
不对吧没有变量类型
private static final sysLogger = SystemLogger.getInstance();
参数与变量同名
public void transferFunds(String from, String to, int amount)
CustomerAccount from = findAccount(fromAcct);
CustomerAccount to = findAccount(toAcct);
private static final sysLogger = SystemLogger.getInstance();
参数与变量同名
public void transferFunds(String from, String to, int amount)
CustomerAccount from = findAccount(fromAcct);
CustomerAccount to = findAccount(toAcct);
3 楼
zcq100
2009-10-07
非常好,谢谢楼主
2 楼
Fire_Iter
2009-10-06
这么详尽的帖子没人顶。支持!谢谢了!LZ
1 楼
YiSingQ
2009-10-06
LZ。。。写得很详细啊。。。学习的好资料。。。
相关推荐
吃饭识别数据集,平均正确识别率在89.8%,1710张原始图片,可识别人是否正在吃饭,支持pasical voc xml格式标注
SR-201网络继电器手机端软件
从零构建:无刷永磁电机FOC驱动控制与DSP外设配置的集成实现方案,从零到一的无刷永磁电机FOC驱动控制:CCS下DSP外设配置与算法代码生成,从零实现无刷 永磁电机FOC驱动控制。 CCS进行DSP外设配置 对控制算法生成代码再到CCS进行集成。 最大限度发挥硬件自由度的同时加快算法的部署实现 ,核心关键词: 1. 无刷永磁电机 2. FOC驱动控制 3. CCS外设配置 4. DSP控制算法生成 5. 硬件自由度 6. 算法部署实现 以上关键词用分号分隔为:无刷永磁电机; FOC驱动控制; CCS外设配置; DSP控制算法生成; 硬件自由度; 算法部署实现。,无刷永磁电机FOC驱动控制:CCS下DSP外设配置与算法代码集成
基于SOC的Buck-Boost电路锂电池主动均衡Simulink仿真与四节电池模型研究,基于SOC的Buck-Boost电路锂电池均衡Simulink仿真及四节电池主动均衡策略与Battery模型研究,buckboost电路 基于soc 锂电池均衡simulink仿真 主动均衡 四节电池 battery模型 ,buckboost电路; 基于soc的锂电池均衡; simulink仿真; 主动均衡; 四节电池组; battery模型; 锂电池均衡策略,基于SOC的Buck-Boost电路锂电池主动均衡仿真研究:四节电池模型及均衡策略
资源简介: 本资源包提供完整的OCL,OTL,甲乙类功率放大电路仿真。 适用人群: 电子工程专业学生(模电课程设计) 硬件开发工程师(功放电路预研) 电子竞赛参赛者(快速验证方案) 创客爱好者(DIY音响系统开发) 使用说明: 解压后使用Multisim打开.ms14文件(原文件为.ms9文件) 按F5运行交互式仿真 右键示波器界面导出测量数据 修改负载电阻值需同步调整偏置电压
安卓模拟器开发资源包下载10
吃饭识别数据集,平均正确识别率在89.8%,1710张原始图片,可识别人是否正在吃饭,支持darknet格式标注
光污染-美赛论文(细节版)
三菱电梯主板参数详解:地址表、地址码与功能调整,包括楼层显示、基站设置等,三菱电梯主板参数详解:地址表、地址码与功能调整,包括楼层显示、基站设置等,三菱电梯主板地址表参数 三菱电梯地址码, KCD-116主板地址参数, MAXIEZ电梯主板地址参数, VFGLC电梯主板地址参数, 可以修改电梯楼层显示、基站、强迫关门、消防功能、开关门时间等参数,如B1层显示改成-1等 ,三菱电梯; 主板地址表参数; KCD-116主板地址码; MAXIEZ电梯; VFGLC电梯主板; 楼层显示; 基站设置; 强迫关门; 消防功能; 开关门时间。,三菱电梯主板参数详解:修改功能与地址码设置指南
现在最方便的用于创建各种知识库。
扑克牌识别数据集,可识别A-K所有的牌字母 1850张原始图,正确识别率可达98.7%,pasical voc xml格式标注
西门子S7-1500PLC博途程序实例详解:滤液生化处理项目之电气自控与WINCC监控画面,西门子S7-1500PLC博途程序实例详解:滤液生化处理项目之电气自控与WINCC监控画面设计,西门子S7-1500PLC博途程序实例。 S7-1500博图程序水处理项目,具体为滤液生化段处理项目,文件内容有博途V16程序及本项目电气自控图纸。 送WINCC7.5画面:MBR系统,加药系统,电气系统及数据系统。 ,S7-1500 PLC; 博途程序; 水处理项目; 滤液生化处理; 博途V16程序; 电气自控图纸; WINCC7.5画面; MBR系统; 加药系统; 数据系统。,西门子S7-1500 PLC博途程序实例:水处理项目应用及电气自控方案
个人经导师指导并认可通过的设计项目,评审分98分,项目中的源码都是经过本地编译过可运行的,都经过严格调试,确保可以运行!提供完整的部署教程和设计文档,方便使用。主要针对计算机相关专业的正在做毕业设计大作业的学生和需要项目实战练习的学习者,资源项目的难度比较适中,内容都是经过助教老师审定过的能够满足学习、使用需求,如果有需要的话可以放心下载使用。 主要用到的技术: java:强制使用面向对象编程(OOP),支持封装、继承、多态和抽象。 Servlet:运行在服务端的Java程序,通过实现javax.servlet.Servlet接口处理HTTP请求和响应,作为JavaWeb的核心控制器,负责动态资源生成和请求逻辑调度 jsp:基于HTML的动态网页技术,允许嵌入Java代码片段(如<% %>),用于简化视图层开发,本质是编译为Servlet的服务器端模板 Mysql:开源关系型数据库管理系统,支持ACID事务和SQL标准,通过JDBC与Java程序连接,适用于Web应用的高并发数据存储与查询
IMG_20250227_181351.jpg
PowerDesigner 16.5 是一款企业级建模与设计工具,支持多种数据建模技术,包括概念数据模型(CDM)、逻辑数据模型(LD)和物理数据模型(PDM)。它采用模型驱动的方法,将业务与 IT 结合,帮助企业设计和管理复杂的业务架构,确保业务流程的顺畅运行。该工具适用于数据库设计与管理、业务流程优化、系统开发等领域,支持多种关系型数据库管理系统(RDBMS),如 Oracle、IBM、Microsoft、Sybase、MySQL 等。
Grafana 是一个开源的数据可视化和监控平台,以下是它的主要功能: 数据可视化 将数据转为图表(如折线图、柱状图、仪表盘),直观展示趋势和状态。 支持自定义布局,创建个性化监控仪表板。 多数据源支持 连接多种系统,如 Zabbix、Prometheus、MySQL、Elasticsearch 等,统一管理数据。 可实时查询和展示来自不同来源的数据。 监控与告警 实时监控服务器、应用或业务指标(如 CPU 使用率、数据库连接数)。 设置阈值触发告警,通过邮件、Slack 等通知用户。 数据分析 提供查询语言和函数(如平均值、最大值),分析历史和实时数据。 支持时间序列数据的高效处理。 权限管理 支持用户和团队权限设置,适合多人协作或企业使用。 企业版提供更细致的访问控制。 扩展性 通过插件(如 Zabbix 插件)扩展功能。 支持 API 集成,自动化配置和管理。
做为学习项目可以学习到 :1.串口通讯的打开,发送数据,接收数据,16进制发送,16进制接收。2.界面布局。3.信号与槽的绑定和解绑。4.控件数据的动态绑定等。 串口通信作为嵌入式开发和硬件调试的重要工具,在工业控制、物联网等领域广泛应用。本文将手把手教你使用QT框架实现一个跨平台的串口调试助手,支持Windows/Linux/macOS系统。
【毕业设计】基于Python的Django-html基于深度学习的聊天机器人设计源码(完整前后端+mysql+说明文档+LW+PPT).zip
STM32F10X的IAP工程中YModem接收函数代码。
基于蒙特卡洛模拟的分布式电源(风光)概率潮流Matlab仿真研究:IEEE 33节点系统的分析与应用(包括牛拉法潮流计算与电压特性前后对比),基于蒙特卡洛方法的分布式电源(风光)概率潮流计算Matlab仿真研究:IEEE 33节点案例分析,包含风速光照概率密度图与电压幅值对比图分析,牛拉法潮流计算及matpower参考版本。,基于蒙特卡洛的含分布式电源(风光)的概率潮流计算Matlab仿真,IEEE33节点,采用牛拉法潮流计算 风速光照的概率密度图和网损图以及电压幅值前后对比图。 包括普通版本和matpower可参考。 ,基于蒙特卡洛;分布式电源;概率潮流计算;Matlab仿真;IEEE33节点;牛拉法潮流计算;风速光照概率密度图;网损图;电压幅值对比图;普通版本;matpower。,基于Monte Carlo的分布式风光电源潮流计算:Matlab仿真及网损电压对比图解