在前面的文章中已经和你们分享过关于spring IOC的知识,已经经过他的实现机制。咱们都知道spring的两大核心:AOP(面向切面)和IOC(控制反转),本篇咱们就一块儿学习一下AOP的知识的。html
这里分享一个问题?当咱们软件开发完成后,须要给每个方法添加操做日志,咱们怎么操做呢?我想最简单的方法就是在每个方法的开始前将咱们的日志逻辑加入,固然这是最直接的一种方法,可是他的缺点也是很明细,若是咱们的方法有不少,添加这个日志逻辑就须要不少的工做量,显然这是一种不可取的方式。如何更好的解决这个问题呢?spring很好的帮咱们处理了这个难点,经过切面编程,咱们能够在咱们须要的切面添加相应的业务逻辑已达到咱们须要的效果。java
接下来开始咱们的内容,AOP的实现借助于JAVA的动态代理知识,我先经过动态代理的方式为你们介绍一下AOP的实现原理,以便你们更好的理解。git
每个动态代理类都必需要实现InvocationHandler这个接口,而且每一个代理类的实例都关联到了一个handler,当咱们经过代理对象调用一个方法的时候,这个方法的调用就会被转发为由InvocationHandler这个接口的 invoke 方法来进行调用。咱们来看看InvocationHandler这个接口的惟一一个方法 invoke 方法:github
Object invoke(Object proxy, Method method, Object[] args) throws Throwable
咱们看到这个方法一共接受三个参数,那么这三个参数分别表明什么呢?web
proxy: 指代咱们所代理的那个真实对象
method: 指代的是咱们所要调用真实对象的某个方法的Method对象
args: 指代的是调用真实对象某个方法时接受的参数
这里我以一个添加日志需求为因,实现一个这个过程:spring
public class LogInterception implements InvocationHandler { private Object target; public void setTarget(Object target) { this.target = target; } private void beforeMethod(){ System.out.println("切面添加日志开始"); } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { // TODO Auto-generated method stub beforeMethod(); Object result = method.invoke(target, args); afterMethod(); return result; } private void afterMethod(){ System.out.println("切面添加日志结束"); } }
咱们写好了代理,下面咱们看一下如何将其加入到方法中使用:express
//添加日志管理切面 LogInterception log = new LogInterception(); log.setTarget(userService); IUserService userServiceProxy = (IUserService) Proxy.newProxyInstance(userService.getClass().getClassLoader(), new Class[]{IUserService.class}, log); // IUserService userServiceProxy = (IUserService) Proxy.newProxyInstance(userService.getClass().getClassLoader(), userService.getClass().getInterfaces(), log); boolean flag = userServiceProxy.add();
这里简单为你们解释一下,首先咱们声明动态代理的类,经过咱们对外提供的setTarget方法将咱们的代理类注入,而后通Proxy.newProxyInstance获得咱们代理方法的代理对象,而后经过调用咱们的代理对象实现咱们的动态代理。apache
public class UserAction extends ActionSupport implements ServletRequestAware, ServletResponseAware{ private IUserService userService; private HttpServletRequest request; private HttpSession session; private HttpServletResponse response; /** * @Description 添加用户测试 * @throws IOException * * @author 高尚 * @version 1.0 * @date 建立时间:2017年12月13日 上午10:56:30 */ public void addJdkInterception(){ Map<String, Object> result = new HashMap<String, Object>(); result.put("status", 1); result.put("msg", "操做成功"); System.out.println("action操做开始"); //添加日志管理切面 LogInterception log = new LogInterception(); log.setTarget(userService); IUserService userServiceProxy = (IUserService) Proxy.newProxyInstance(userService.getClass().getClassLoader(), new Class[]{IUserService.class}, log); // IUserService userServiceProxy = (IUserService) Proxy.newProxyInstance(userService.getClass().getClassLoader(), userService.getClass().getInterfaces(), log); boolean flag = userServiceProxy.add(); result.put("data", flag); System.out.println("action操做结束"); //输出到客户端 PrintWriter writer = null; try { if(null != response){ response.setContentType("text/html;charset=UTF-8");// 解决中文乱码 response.setCharacterEncoding("UTF-8"); writer = response.getWriter(); writer.write(JSONObject.toJSONString(result)); }else{ System.out.println(JSONObject.toJSONString(result)); } } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); }finally { if(null != writer){ writer.flush(); writer.close(); } } } public void setUserService(IUserService userService) { this.userService = userService; } @Override public void setServletResponse(HttpServletResponse response) { // TODO Auto-generated method stub this.response = response; } @Override public void setServletRequest(HttpServletRequest request) { // TODO Auto-generated method stub this.request = request; this.session = this.request.getSession(); } }
启动咱们的项目试一把,经过控制台的日志信息,我想你必定能理解java动态代理的机制。编程
经过上面的内容,相信对于你理解spring aop的知识必定颇有帮助。下面咱们来看一下srping aop的知识:浏览器
首先咱们搭建一个简单的spring开发环境:
一、dao接口和实现类
public interface IUserDao { boolean add(); void delete(); } public class UserDaoImpl implements IUserDao { @Override public boolean add() { System.out.println("dao添加操做成功"); return false; } @Override public void delete() { System.out.println("dao删除操做成功"); } }
二、service接口和实现类
public interface IUserService { boolean add(); void delete(); } public class UserServiceImpl implements IUserService { private IUserDao userDao; @Override public boolean add() { System.out.println("service添加操做开始"); userDao.add(); System.out.println("service添加操做结束"); return false; } @Override public void delete() { System.out.println("service删除操做开始"); userDao.delete(); System.out.println("service删除操做结束"); } public void setUserDao(IUserDao userDao) { this.userDao = userDao; } }
三、action实现类
public class UserAction extends ActionSupport implements ServletRequestAware, ServletResponseAware{ private IUserService userService; private HttpServletRequest request; private HttpSession session; private HttpServletResponse response; /** * @Description 添加用户测试 * @throws IOException * * @author 高尚 * @version 1.0 * @date 建立时间:2017年12月13日 上午10:56:30 */ public void add(){ Map<String, Object> result = new HashMap<String, Object>(); result.put("status", 1); result.put("msg", "操做成功"); System.out.println("action操做开始"); boolean flag = userService.add(); result.put("data", flag); System.out.println("action操做结束"); //输出到客户端 PrintWriter writer = null; try { if(null != response){ response.setContentType("text/html;charset=UTF-8");// 解决中文乱码 response.setCharacterEncoding("UTF-8"); writer = response.getWriter(); writer.write(JSONObject.toJSONString(result)); }else{ System.out.println(JSONObject.toJSONString(result)); } } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); }finally { if(null != writer){ writer.flush(); writer.close(); } } } /** * @Description 删除用户测试 * @throws IOException * * @author 高尚 * @version 1.0 * @date 建立时间:2017年12月13日 上午10:56:30 */ public void delete(){ Map<String, Object> result = new HashMap<String, Object>(); result.put("status", 1); result.put("msg", "操做成功"); System.out.println("action操做开始"); userService.delete(); result.put("data", true); System.out.println("action操做结束"); //输出到客户端 PrintWriter writer = null; try { if(null != response){ response.setContentType("text/html;charset=UTF-8");// 解决中文乱码 response.setCharacterEncoding("UTF-8"); writer = response.getWriter(); writer.write(JSONObject.toJSONString(result)); }else{ System.out.println(JSONObject.toJSONString(result)); } } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); }finally { if(null != writer){ writer.flush(); writer.close(); } } } public void setUserService(IUserService userService) { this.userService = userService; } @Override public void setServletResponse(HttpServletResponse response) { // TODO Auto-generated method stub this.response = response; } @Override public void setServletRequest(HttpServletRequest request) { // TODO Auto-generated method stub this.request = request; this.session = this.request.getSession(); } }
四、切面逻辑
public class TimeIntercepation { private static final SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS"); public void before(){ System.out.println("执行开始时间:" + sdf.format(new Date())); } public Object around(ProceedingJoinPoint pjp) throws Throwable{ System.out.println("around 执行前"); Object result = pjp.proceed();//得到方法执行后的返回参数 System.out.println("around 执行后"); return result; } public void after(){ System.out.println("执行开始时间:" + sdf.format(new Date())); } public void afterReturning(){ System.out.println("方法正常执行完毕"); } public void throwing(){ System.out.println("方法执行出现异常"); } }
五、spring配置文件
<?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:aop="http://www.springframework.org/schema/aop" xmlns:context="http://www.springframework.org/schema/context" xmlns:tx="http://www.springframework.org/schema/tx" 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-4.3.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.3.xsd"> <bean name="userDao" class="com.hpugs.spring.one.dao.UserDaoImpl" lazy-init="true"></bean> <bean name="userService" class="com.hpugs.spring.one.service.UserServiceImpl"> <property name="userDao" ref="userDao"></property> </bean> <bean name="userAction" class="com.hpugs.spring.one.action.UserAction" scope="prototype"> <property name="userService" ref="userService"></property> </bean> <bean name="timeIntercepation" class="com.hpugs.spring.one.interception.TimeIntercepation" lazy-init="true"></bean> <aop:config> <aop:pointcut expression="execution(* com.hpugs.spring.one.service.*.*(..))" id="serviceIntercepation"/> <aop:aspect id="timeAspect" ref="timeIntercepation"> <aop:before method="before" pointcut-ref="serviceIntercepation"/> <aop:around method="around" pointcut-ref="serviceIntercepation"/> <aop:after-returning method="afterReturning" pointcut-ref="serviceIntercepation"/> <aop:after method="after" pointcut-ref="serviceIntercepation"/> <aop:after-throwing method="throwing" pointcut-ref="serviceIntercepation"/> </aop:aspect> </aop:config> </beans>
这里简单解释一下,aop:config:设置spring切面,添加切面操做;aop:pointcut:声明切面;aop:aspect:添加切面操做
六、status配置
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.3//EN" "http://struts.apache.org/dtds/struts-2.3.dtd"> <struts> <!-- 取消 struts2 动态方法调用 --> <constant name="struts.enable.DynamicMethodInvocation" value="false" /> <!-- 是否启用开发模式 --> <constant name="struts.devMode" value="false" /> <!-- 全部匹配 *.action 的请求都由 struts2 处理 --> <constant name="struts.action.extension" value="action" /> <!-- struts 配置文件改动后,是否从新加载 --> <constant name="struts.configuration.xml.reload" value="true" /> <!-- 设置浏览器是否缓存静态内容 --> <constant name="struts.serve.static.browserCache" value="false" /> <!-- 自动从新加载映射加载 --> <constant name="struts.convention.classes.reload" value="true"/> <!-- action 对象是由Spring负责建立 --> <constant name="struts.objectFactory" value="spring" /> <package name="user" namespace="/" extends="struts-default"> <action name="user_add" class="userAction" method="add"></action> <action name="user_delete" class="userAction" method="delete"></action> </package> </struts>
七、web.xml配置
<context-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:applicationContext*.xml</param-value> </context-param> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener> <filter> <filter-name>struts2</filter-name> <filter-class>org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter</filter-class> </filter> <filter-mapping> <filter-name>struts2</filter-name> <url-pattern>/*</url-pattern> <dispatcher>REQUEST</dispatcher> <dispatcher>FORWARD</dispatcher> </filter-mapping> <web:welcome-file-list> <web:welcome-file>index.jsp</web:welcome-file> </web:welcome-file-list>
八、junit单元测试
@Test public void addTest(){ //注入Spring Bean ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml"); //获取UserAction对象 UserAction userAction = (UserAction) applicationContext.getBean("userAction"); //都有添加操做 userAction.add(); }
九、单元测试结果:
action操做开始
执行开始时间:2018-01-18 15:34:14.412
around 执行前
service添加操做开始
dao添加操做成功
service添加操做结束
around 执行后
方法正常执行完毕
执行开始时间:2018-01-18 15:34:14.412
action操做结束
{"msg":"操做成功","data":false,"status":1}
到这里咱们经过xml的方式就实现了方法操做时间切面方法的注入。
最后在为你们介绍一下关于spring annotation添加切面的实现:
首先使咱们的spring配置文件,添加注解事务
<!-- <bean name="timeIntercepation" class="com.hpugs.spring.one.interception.TimeIntercepation" lazy-init="true"></bean> --> <context:annotation-config></context:annotation-config> <context:component-scan base-package="com.hpugs.spring.one.interception"></context:component-scan> <aop:aspectj-autoproxy proxy-target-class="true"></aop:aspectj-autoproxy>
这里解释一下,咱们能够经过bean的方式将咱们的代理方法注入,可是不推荐你们使用,这里推荐你们使用包扫描的方式进行代理方法注入。
经过注解的方式进行切面声明:
@Aspect @Component public class TimeIntercepation { private static final SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); @Pointcut("execution(* com.hpugs.spring.one.service.*.*(..))") public void myMethod(){} @Before("execution(* com.hpugs.spring.one.service.*.*(..))") public void before(){ System.out.println("执行开始时间:" + sdf.format(new Date())); } @Around("myMethod()") public Object around(ProceedingJoinPoint pjp) throws Throwable{ System.out.println("around 执行前"); Object result = pjp.proceed();//得到方法执行后的返回参数 System.out.println("around 执行后"); return result; } @After("execution(* com.hpugs.spring.one.service.*.*(..))") public void after(){ System.out.println("执行开始时间:" + sdf.format(new Date())); } @AfterReturning("myMethod()") public void afterReturning(){ System.out.println("方法正常执行完毕"); } @AfterThrowing("myMethod()") public void throwing(){ System.out.println("方法执行出现异常"); } }
这里解释一下myMethod方法,若是咱们的方法共用切面时,咱们能够经过这种方式将面声明进行复用。
关于Spring AOP的内容就和你们探讨到这里,以上源代码下载地址:https://github.com/hpugs/Spring-aop