博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Spring AOP
阅读量:5060 次
发布时间:2019-06-12

本文共 12389 字,大约阅读时间需要 41 分钟。

  在前面的文章中已经和大家分享过关于spring IOC的知识,已经通过他的实现机制。我们都知道spring的两大核心:AOP(面向切面)和IOC(控制反转),本篇我们就一起学习一下AOP的知识的。

  这里分享一个问题?当我们软件开发完成后,需要给每一个方法添加操作日志,我们怎么操作呢?我想最简单的方法就是在每一个方法的开始前将我们的日志逻辑加入,当然这是最直接的一种方法,但是他的缺点也是很明细,如果我们的方法有很多,添加这个日志逻辑就需要很多的工作量,显然这是一种不可取的方式。如何更好的解决这个问题呢?spring很好的帮我们处理了这个难点,通过切面编程,我们可以在我们需要的切面添加相应的业务逻辑已达到我们需要的效果。

  接下来开始我们的内容,AOP的实现借助于JAVA的动态代理知识,我先通过动态代理的方式为大家介绍一下AOP的实现原理,以便大家更好的理解。

  每一个动态代理类都必须要实现InvocationHandler这个接口,并且每个代理类的实例都关联到了一个handler,当我们通过代理对象调用一个方法的时候,这个方法的调用就会被转发为由InvocationHandler这个接口的 invoke 方法来进行调用。我们来看看InvocationHandler这个接口的唯一一个方法 invoke 方法:

Object invoke(Object proxy, Method method, Object[] args) throws Throwable

  我们看到这个方法一共接受三个参数,那么这三个参数分别代表什么呢?

proxy:  指代我们所代理的那个真实对象method:  指代的是我们所要调用真实对象的某个方法的Method对象args:  指代的是调用真实对象某个方法时接受的参数

  这里我以一个添加日志需求为因,实现一个这个过程:

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("切面添加日志结束");    }}

  我们写好了代理,下面我们看一下如何将其加入到方法中使用:

  

//添加日志管理切面        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得到我们代理方法的代理对象,然后通过调用我们的代理对象实现我们的动态代理。

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
result = new HashMap
(); 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开发环境:

  1、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删除操作成功");    }}

  2、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;    }    }

  3、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
result = new HashMap
(); 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
result = new HashMap
(); 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(); } }

  4、切面逻辑

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("方法执行出现异常");    }    }

  5、spring配置文件

  这里简单解释一下,aop:config:设置spring切面,添加切面操作;aop:pointcut:声明切面;aop:aspect:添加切面操作

  6、status配置

  7、web.xml配置

contextConfigLocation
classpath:applicationContext*.xml
org.springframework.web.context.ContextLoaderListener
struts2
org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter
struts2
/*
REQUEST
FORWARD
index.jsp

  8、junit单元测试

@Test    public void addTest(){        //注入Spring Bean        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");        //获取UserAction对象        UserAction userAction = (UserAction) applicationContext.getBean("userAction");        //都有添加操作        userAction.add();    }

  9、单元测试结果:

action操作开始执行开始时间:2018-01-18 15:34:14.412around 执行前service添加操作开始dao添加操作成功service添加操作结束around 执行后方法正常执行完毕执行开始时间:2018-01-18 15:34:14.412action操作结束{"msg":"操作成功","data":false,"status":1}

  到这里我们通过xml的方式就实现了方法操作时间切面方法的注入。

  最后在为大家介绍一下关于spring annotation添加切面的实现:

  首先使我们的spring配置文件,添加注解事务

  这里解释一下,我们可以通过bean的方式将我们的代理方法注入,但是不推荐大家使用,这里推荐大家使用包扫描的方式进行代理方法注入。

  通过注解的方式进行切面声明:

@Aspect@Componentpublic 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://www.cnblogs.com/AndroidJotting/p/8310666.html

你可能感兴趣的文章
Linux 根文件系统制作
查看>>
IOS--沙盒机制
查看>>
My.Ioc 的性能
查看>>
使用 JointCode.Shuttle 访问任意 AppDomain 的服务
查看>>
hdoj 1846 Brave Game(巴什博弈)
查看>>
Round #345 B. Beautiful Paintings(Div.2)
查看>>
51nod 1018排序
查看>>
sqlite的坑
查看>>
digitalocean --- How To Install Apache Tomcat 8 on Ubuntu 16.04
查看>>
linux swoole
查看>>
An Easy Problem?! - POJ 2826(求面积)
查看>>
【题解】[P4178 Tree]
查看>>
Jquery ui widget开发
查看>>
css3实现循环执行动画,且动画每次都有延迟
查看>>
更改git仓库地址
查看>>
有标号DAG计数 [容斥原理 子集反演 组合数学 fft]
查看>>
Recipe 1.4. Reversing a String by Words or Characters
查看>>
Rule 1: Make Fewer HTTP Requests(Chapter 1 of High performance Web Sites)
查看>>
sql注入
查看>>
「破解」Xposed强
查看>>