Spring框架基础2

Posted XDZY

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Spring框架基础2相关的知识,希望对你有一定的参考价值。

Spring框架基础2

测试Spring的AOP思想和注解的使用

 

导包(在前面的基础上添加)

 

SpringAOP名词解释

AOP编程思想:横向重复代码,纵向抽取;就是说多个地方重复的代码可以抽取出来公用(过滤器等可以体现)
动态代理:动态代理可以体现AOP思想;对目标方法进行增强
SpringAOP开发:封装了动态代理代码(包括cglib代理),可以对任何类进行代理增强
Joinpoint(连接点):目标对象中,所有可以增强的方法
Pointcut(切入点):目标对象,已经增强的方法
Advice(通知/增强):增强的代码
Target(目标对象):被代理对象
Weaving(织入):将通知应用到切入点的过程
Proxy(代理):将通知织入到目标对象之后,形成代理对象
aspect(切面):切入点+通知

 

创建配置文件(如图由上至下)

<?xml version="1.0" encoding="UTF-8"?>
<!-- 使用某个标签之前要导入相应的约束 -->
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns="http://www.springframework.org/schema/beans"
       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-4.2.xsd
       http://www.springframework.org/schema/context
       http://www.springframework.org/schema/context/spring-context-4.2.xsd
       http://www.springframework.org/schema/aop
       http://www.springframework.org/schema/aop/spring-aop-4.2.xsd ">

    <!-- 配置目标对象 -->
    <bean name="userService" class="com.service.UserServiceImpl"></bean>
    <!-- 配置通知对象 -->
    <bean name="myAdvice" class="com.anno.MyAdvice"></bean>
    <!-- 将通知织入目标对象,利用注解实现 -->
    <aop:aspectj-autoproxy></aop:aspectj-autoproxy>
</beans>
<?xml version="1.0" encoding="UTF-8"?>
<!-- 使用某个标签之前要导入相应的约束 -->
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns="http://www.springframework.org/schema/beans"
       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-4.2.xsd
       http://www.springframework.org/schema/context
       http://www.springframework.org/schema/context/spring-context-4.2.xsd
       http://www.springframework.org/schema/aop
       http://www.springframework.org/schema/aop/spring-aop-4.2.xsd ">

    <!-- 配置目标对象 -->
    <bean name="userServiceTarget" class="com.service.UserServiceImpl"></bean>
    <!-- 配置通知对象 -->
    <bean name="myAdvice" class="com.aspect.MyAdvice"></bean>
    <!-- 将通知织入目标对象 -->
    <aop:config>
        <!-- 配置切入点,即需要加强功能的方法
         public void com.service.UserServiceImpl.save()
         void com.service.UserServiceImpl.save()
         * com.service.UserServiceImpl.save()
         * com.service.UserServiceImpl.*
         * com.service.UserServiceImpl.*(..)
         * com.service.*ServiceImpl.*(..)-->
        <aop:pointcut id="pc" expression="execution(* com.service.*ServiceImpl.*(..))"/>
        <aop:aspect ref="myAdvice">
            <!-- 将myAdvice的before切入到UserServiceImpl.save() -->
            <aop:before method="before" pointcut-ref="pc"/>
            <aop:after-returning method="afterReturning" pointcut-ref="pc"/>
            <aop:around method="around" pointcut-ref="pc"/>
            <aop:after-throwing method="afterException" pointcut-ref="pc"/>
            <aop:after method="after" pointcut-ref="pc"/>
        </aop:aspect>
    </aop:config>
</beans>
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns="http://www.springframework.org/schema/beans"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans-4.2.xsd
       http://www.springframework.org/schema/context
       http://www.springframework.org/schema/context/spring-context-4.2.xsd ">

    <!-- 扫描相应包下的类的所有注解 -->
    <!-- 会扫描该包下的所有子孙类 -->
    <context:component-scan base-package="com.bean"/>

    <bean name="car2" class="com.bean.Car">
        <property name="name" value="玛莎拉蒂"/>
        <property name="color" value="red"/>
    </bean>

</beans>

 

相应的实体类与接口

package com.bean;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

/**
 * @author: XDZY
 * @date: 2018/9/5 23:38
 * @description: 车辆实体类
 */
@Component("car")
public class Car {
    @Value("兰博基尼")
    private String name;
    @Value("red")
    private String color;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getColor() {
        return color;
    }

    public void setColor(String color) {
        this.color = color;
    }

    @Override
    public String toString() {
        return "Car{" +
                "name=\'" + name + \'\\\'\' +
                ", color=\'" + color + \'\\\'\' +
                \'}\';
    }
}
package com.bean;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import javax.annotation.Resource;

/**
 * @author: XDZY
 * @date: 2018/9/5 19:31
 * @description: 用户实体类
 * 通过注解获取对象
 * "@Component("user")==<bean name="user" class="com.bean.User"/>"
 */
@Component("user")
//@Service("user")指定为service层
//@Controller("user")指定为web层
//@Repository("user")指定为dao层
//单例还是多例
//@Scope(scopeName = "singleton")
public class User {
    //通过反射设置值,破坏了封装性
    @Value("xdzy")
    private String name;
    @Value("15")
    private int age;

    //自动配置car属性;但是有多个对象时,不知道获取哪个
    //@Autowired
    //指定哪个对象
    //@Qualifier("car2")
    //手动注解使用哪个car
    @Resource(name = "car2")
    private Car car;

    //创建对象前调用
    @PostConstruct
    public void init() {
        System.out.println("初始化方法");
    }

    //对象销毁前调用
    @PreDestroy
    public void destroy() {
        System.out.println("销毁方法");
    }

    public Car getCar() {
        return car;
    }

    public void setCar(Car car) {
        this.car = car;
    }

    public String getName() {
        return name;
    }

    //通过set设置值,推荐使用
    @Value("xdzy")
    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "User{" +
                "name=\'" + name + \'\\\'\' +
                ", age=" + age +
                ", car=" + car +
                \'}\';
    }
}
package com.service;

/**
 * @author: XDZY
 * @date: 2018/9/6 20:21
 * @description:
 */
public interface UserService {
    void save();

    void del();

    void update();

    void find();
}
package com.service;

/**
 * @author: XDZY
 * @date: 2018/9/6 20:22
 * @description:
 */
public class UserServiceImpl implements UserService {
    @Override
    public void save() {
        System.out.println("增加用户");
    }

    @Override
    public void del() {
        System.out.println("删除用户");
    }

    @Override
    public void update() {
        System.out.println("修改用户");
    }

    @Override
    public void find() {
        System.out.println("查询用户");
    }
}

 

JDK动态代理

package com.service;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

/**
 * @author: XDZY
 * @date: 2018/9/6 20:25
 * @description: JDK动态代理
 * 被代理对象必须要实现接口,才能产生代理对象,如果没有接口将不能使用动态代理技术
 * 动态代理可对方法进行增强,如增加事务的打开与提交
 * 个人理解:它是对service实现类里所有的方法进行了增强;
 * 在不破坏原有结构的情况下,生成动态代理对象,对原有方法进行增强
 */
public class UserServiceProxyFactory implements InvocationHandler {
    private UserService us;

    public UserServiceProxyFactory(UserService us) {
        this.us = us;
    }

    public UserService getUserServiceProxy() {
        //生成动态代理
        UserService userProxy = (UserService) Proxy.newProxyInstance(UserServiceProxyFactory.class.getClassLoader(),
                UserServiceImpl.class.getInterfaces(),
                this);
        //返回一个动态代理对象
        return userProxy;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("打开事务");
        Object invoke = method.invoke(us, args);
        System.out.println("提交事务");
        return invoke;
    }
}

 

Cglib代理

package com.service;

import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;

/**
 * @author: XDZY
 * @date: 2018/9/6 20:25
 * @description: Cglib动态代理
 * 可以对任何类生成代理,对目标对象进行继承代理
 */
public class UserServiceProxyFactory2 implements MethodInterceptor {
    public UserService getUserServiceProxy() {
        //生成代理对象
        Enhancer en = new Enhancer();
        //对谁进行代理
        en.setSuperclass(UserServiceImpl.class);
        //代理要做什么
        en.setCallback(this);
        //创建代理对象
        UserService us = (UserService) en.create();
        return us;
    }

    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        //打开事务
        System.out.println("打开事务");
        //调用原有方法
        Object returnValue = methodProxy.invokeSuper(o, objects);
        //提交事务
        System.out.println("提交事务");
        return returnValue;
    }
}

 

通知类

package com.anno;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;

/**
 * @author: XDZY
 * @date: 2018/9/7 08:23
 * @description: 利用注解实现通知
 * "@Aspect:表示该类是一个通知类
 */
@Aspect
public class MyAdvice {
    //方便管理切入点
    @Pointcut("execution(* com.service.*ServiceImpl.*(..))")
    public void pc() {
    }

    //配置通知,并指定织入到哪去
    //前置通知:目标方法运行之前
    @Before("MyAdvice.pc()")
    public void before() {
        System.out.println("前置通知");
    }

    //后置通知(如果出现异常不会调用):之后
    @AfterReturning("execution(* com.service.*ServiceImpl.*(..))")
    public void afterReturning() {
        System.out.println("后置通知(如果出现异常不会调用)");
    }

    //环绕通知:之前之后
    @Around("execution(* com.service.*ServiceImpl.*(..))")
    public Object around(ProceedingJoinPoint pjp) throws Throwable {
        System.out.println("环绕通知之前部分");
        //调用目标方法
        Object proceed = pjp.proceed();
        System.out.println("环绕通知之后部分");
        return proceed;
    }

    //异常拦截通知:出现异常调用
    @AfterThrowing("execution(* com.service.*ServiceImpl.*(..))")
    public void afterException() {
        System.out.println("出现异常调用");
    }

    //后置通知(无论是否出现异常都会调用):之后
    @After("execution(* com.service.*ServiceImpl.*(..))")
    public void after() {
        System.out.println("后置通知(无论是否出现异常都会调用)");
    }
}
package com.aspect;

import org.aspectj.lang.ProceedingJoinPoint;

/**
 * @author: XDZY
 * @date: 2018/9/7 08:23
 * @description: 通知
 * 前置通知:目标方法运行之前
 * 后置通知(如果出现异常不会调用):之后
 * 环绕通知:之前之后
 * 异常拦截通知:出现异常调用
 * 后置通知(无论是否出现异常都会调用):之后
 */
public class MyAdvice {
    //前置通知:目标方法运行之前
    public void before() {
        System.out.println("前置通知");
    }

    //后置通知(如果出现异常不会调用):之后
    public void afterReturning() {
        System.out.println("后置通知(如果出现异常不会调用)");
    }

    //环绕通知:之前之后
    public Object around(ProceedingJoinPoint pjp) throws Throwable {
        System.out.println("环绕通知之前部分");
        //调用目标方法
        Object proceed = pjp.proceed();
        System.out.println("环绕通知之后部分");
        return proceed;
    }

    //异常拦截通知:出现异常调用
    public void afterException() {
        System.out.println("出现异常调用");
    }

    //后置通知(无论是否出现异常都会调用):之后
    public void after() {
        System.out.println("后置通知(无论是否出现异常都会调用)");
    }
}

 

测试类

package com.test;

import com.service.UserService;
import com.service.UserServiceImpl;
import com.service.UserServiceProxyFactory;
import com.service.UserServiceProxyFactory2;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

/**
 * @author: XDZY
 * @date: 2018/9/6 09:32
 * @description: 测试注解获取对象
 * "@RunWith:可以帮助我们创建容器,这样xml地址改动,测试方法不用全部修改
 * "@ContextConfiguration:指定容器位置
 * 注意junit版本问题
 */
//@RunWith(SpringJUnit4ClassRunner.class)
//@ContextConfiguration("classpath:resources/applicationContext.xml")
public class Demo1 {
    /*@Resource(name = "user")
    private User user;

    @Test
    public void test(){
        System.out.println(user);
    }*/

    //JDK动态代理
    @Test
    public void test1() {
        UserService us = new UserServiceImpl();
        UserServiceProxyFactory factory = new UserServiceProxyFactory(us);
        UserService userProxy = factory.getUserServiceProxy();
        userProxy.save();

        //代理对象和被代理对象实现了相同的接口(false)
        System.out.println(userProxy instanceof UserServiceImpl);
    }

    //Cglib动态代理
    @Test
    public void test2() {
        UserServiceProxyFactory2 factory = new UserServiceProxyFactory2();
        UserService userProxy = factory.getUserServiceProxy();
        userProxy.save();

        //代理对象继承了被代理对象(true)
        System.out.println(userProxy instanceof UserServiceImpl);
    }

    //通知织入
    @Test
    public void test() {
        //创建容器对象
        ApplicationContext ac = new ClassPathXmlApplicationContext("com/aop/applicationContext.xml");
        //获取user对象
        UserService userService = (UserService) ac.getBean("userServiceTarget");
        userService.save();
    }

    //注解通知织入
    @Test
    public void test4() {
        //创建容器对象
        ApplicationContext ac = new ClassPathXmlApplicationContext("com/anno/applicationContext.xml");
        //获取user对象
        UserService userService = (UserService) ac.getBean("userService");
        userService.save();
    }
}

 

以上是关于Spring框架基础2的主要内容,如果未能解决你的问题,请参考以下文章

初识Spring源码 -- doResolveDependency | findAutowireCandidates | @Order@Priority调用排序 | @Autowired注入(代码片段

Sping基础

Spring+SpringMVC+MyBatis+Maven框架整合

java:Spring框架1(基本配置,简单基础代码实现)

零基础学习-spring框架

Spring框架基础