java回顾:springIOC注解开发、整合Junit、回顾动态代理

目录

一、IOC相关注解

1.1、启动注解功能

1.2、定义bean的常用注解@Component @Controller @Service @Repository

1.3、bean的属性数据注入常用注解

1.3.1 基本类型和String属性注入—@Value

1.3.2 properties文件中属性注入—@Value

1.3.3 bean的引用类型属性注入

1.3.4 作用范围(单例多例)和生命周期相关注解

1.4、纯注解格式

二、整合Junit

三、回顾动态代理

3.1、基于接口的动态代理(JDK):

3.2、无对象的动态代理(JDK):

3.3、基于子类的动态代理(cglib第三方):


一、IOC相关注解

spring加载资源时使用注解的形式替代xml配置,将繁杂的spring配置文件从工程中彻底消除掉,简化书写。

注解驱动的弊端:

  • 为了达成注解驱动的目的,可能会将原先很简单的书写,变的更加复杂

  • XML中配置第三方开发的资源是很方便的,但使用注解驱动无法在第三方开发的资源中进行编辑,因此会增大开发工作量

1.1、启动注解功能

  • 在spring XML配置文件中启动注解扫描,加载类中配置的注解项

    <context:component-scan base-package="packageName"/>
  • 说明:

    • 在进行包所扫描时,会对配置的包及其子包中所有文件进行扫描;

    • 扫描过程是以文件夹递归迭代的形式进行的;

    • 扫描过程仅读取合法的java文件

    • 扫描时仅读取spring可识别的注解

    • 扫描结束后会将可识别的有效注解转化为spring对应的资源加入IoC容器

  • 注意:

    • 无论是注解格式还是XML配置格式,最终都是将资源加载到IoC容器中,差别仅仅是数据读取方式不同

    • 从加载效率上来说注解优于XML配置文件

 1.2、定义bean的常用注解@Component @Controller @Service @Repository

  • 名称:@Component @Controller @Service @Repository
  • 类型:类注解
  • 位置:类的定义上方
  • 作用:设置该类为spring管理的bean
  • 范例:

@Component: 用于创建对象,将创建的对象存放到IOC容器中
       value: 设置存放到IOC容器中id的值,如果不写默认为当前类的类名称,首字母小写
  以下三个注解跟@Component注解的作用一样,只不过下面三个注解提供了更加详细的语义化(见名知意)
@Controller : 将web层的类创建对象存放到IOC容器中
@Service : 将serice层的类创建对象存放到IOC容器中
@Repository : 将dao层的类创建对象存放到IOC容器中 

@Component("userService")
public class AccountServiceImpl{}
  • 以上注解相当于xml 中的bean标签:
    • <bean id="userService" class="com.itheima.service.impl.UserServiceImpl"></bean>
    • @Controller、@Service 、@Repository是@Component的衍生注解,功能同@Component

    • @Controller(控制层注解)、@Service(服务层注解) 、@Repository(持久层注解)只是提供了更加明确的语义化(见名知意),精确指出是哪一层的对象,但不是强制要求的;

  • 相关属性

    • value(默认):定义bean的访问id

测例:

<!--开启注解扫描,引入context约束-->
    <context:component-scan base-package="hhyZhuJie"/>
/**
 * @Component: 用于创建对象,将创建的对象存放到IOC容器中
 *      value: 设置存放到IOC容器中id的值,如果不写默认为当前类的类名称,首字母小写
 * 以下三个注解跟@Component注解的作用一样,只不过下面三个注解提供了更加详细的语义化(见名知意)
 * @Controller : 将web层的类创建对象存放到IOC容器中
 * @Service : 将serice层的类创建对象存放到IOC容器中
 * @Repository : 将dao层的类创建对象存放到IOC容器中
 */
//@Component("ZJService")
@Service("ZJService")
//@Repository("ZJService")
//@Controller("ZJService")
public class JZServiceImpl implements ZJService {

    public JZServiceImpl() {
        System.out.println("JZServiceImpl无参构造被调用");
    }

    @Override
    public void add() {
        System.out.println("JZServiceImpl的add方法被调用");
    }
}
    //springIOC注解开发测例
    @Test
    public void t1() {
        ApplicationContext c = new ClassPathXmlApplicationContext("springIOCzhujie.xml");
        ZJService zjService = c.getBean("ZJService", ZJService.class);
        zjService.add();
    }

 1.3、bean的属性数据注入常用注解

以下注解它们就相当于:
<property name="userDao" ref="userDao" 或 value="基本类型数据或String"></property>
------------------------
这些注解使用在属性上,用于给对象中的属性进行赋值
引用类型:
    @Autowired
    @Qualifier
    @Resource
基本类型+String:
    @Value 

1.3.1 基本类型和String属性注入—@Value

@Value: 用于注入基本类型和String的
    作用:注入基本类型和String的数据
    属性:   
        value:指定数据的内容,它可以支持Spring的EL表达式(SPEL),写法就是${表达式}
SpEL表达式:
    作用: 从IOC容器中获取对应的值
    格式: ${key}
        属性值来自于properties配置文件
        
加载properties配置文件中的信息存放到容器中
    <context:property-placeholder location="classpath:jdbc.properties"></context:property-placeholder>
    
​ 注意: 当我们使用注解注入数据时,构造器和set方法都不是必须的了. 

测例一:

@Service("ZJService")
@ToString
public class JZServiceImpl implements ZJService {

    @Value("迪迦")
    private String name;
    @Value("18")
    private Integer age;

    public JZServiceImpl() {
        System.out.println("JZServiceImpl无参构造被调用");
    }

    @Override
    public void add() {
        System.out.println("JZServiceImpl的add方法被调用");
    }
}


    @Test
    public void 测试Value注解() {
        ApplicationContext c = new ClassPathXmlApplicationContext("springIOCzhujie.xml");
        ZJService zjService = c.getBean("ZJService", ZJService.class);
        System.out.println("测试Value注解 = " + zjService);
        //测试Value注解 = JZServiceImpl(name=迪迦, age=18)
    }

测例二:获取properties中值

1.3.2 properties文件中属性注入—@Value

配置加载外部Properties资源,将配置文件中的数据存放到IOC容器中

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"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">

    <!--开启注解扫描,引入context约束-->
    <context:component-scan base-package="hhyZhuJie"/>

    <!-- 配置加载外部Properties资源,将配置文件中的数据存放到IOC容器中 -->
    <context:property-placeholder
            location="classpath:springJZ.properties"
            file-encoding="GBK"></context:property-placeholder>
</beans>
springZJ.username=迪迦
springZJ.age=18
@Service("ZJService")
//@Repository("ZJService")
//@Controller("ZJService")
@ToString
public class JZServiceImpl implements ZJService {

//    @Value("迪迦")
    @Value("${springZJ.username}")
    private String name;
//    @Value("18")
    @Value("${springZJ.age}")
    private Integer age;

    public JZServiceImpl() {
        System.out.println("JZServiceImpl无参构造被调用");
    }

    @Override
    public void add() {
        System.out.println("JZServiceImpl的add方法被调用");
    }
}


    @Test
    public void 测试Value注解() {
        ApplicationContext c = new ClassPathXmlApplicationContext("springIOCzhujie.xml");
        ZJService zjService = c.getBean("ZJService", ZJService.class);
        System.out.println("测试Value注解 = " + zjService);
        //测试Value注解 = JZServiceImpl(name=迪迦, age=18)
    }

1.3.3 bean的引用类型属性注入

@Autowired

 Autowired: 在注入数据时,是按照bean类型注入的
     原理:
         被注入的对象,来自IOC容器
         如果IOC容器中只有一个此类型的对象,直接注入
         如果IOC容器中有多个此类型的对象,按照IOC容器中对象的名称注入,如果名称不匹配报错
     属性:
         required: 
             true: 此对象必须注入成功,若不成功则报错. 默认值
             false: 可以注入不成功

@Qualifier

作用:在自动按照类型注入的基础之上,再按照bean的id注入.

@Resource

此注解是JDK提供的,作用和@Autowired+@Qualifier一样
作用:默认按照bean名称注入数据
属性:
    name:指定bean的名称注入数据
    type:指定bean的类型注入数据
注意细节:
    如果没有对应的名称与之匹配,则按照类型注入

使用@Autowired注解,如果某个类如@Service("autowiredService")指定名字,那么定义时名字要相同。

若想取不同名字,则需用@Qualifier注解取相同名字。

 

测例:

//新接口
public interface AutowiredService {
    void add();
}

//新实现类
@Service("autowiredService")
public class AutowiredServiceImpl implements AutowiredService {
    @Override
    public void add() {
        System.out.println("AutowiredService中add方法被调用");
    }
}

@Service("ZJService")
@ToString
public class JZServiceImpl implements ZJService {

    /**
     * IOC容器:
     *      AutowiredService:
     *           AutowiredServiceImpl   accountDaoImpl
     *           AutowiredServiceImpl2  accountDaoImpl2
     * @Autowired: 将IOC容器中的对象注入到属性上
     *          默认按照类型注入,当只有一个该类型对象时,直接注入
     *          如果发现多个同类型的对象,则按照变量名称注入
     *          如果没有一样的变量名则报错
     *     required: 是否必须注入成功(当前属性需要结合@Qualifier使用)
     *          true: 必须注入成功,否则报错 (默认值)
     *          false: 可以注入不成功,如果注入不成功,则为null
     *  @Qualifier: 指定注入的对象的名称
     *
     *  @Resource:
     *      默认按照名称注入,如果IOC容器中没有对应名称的对象,则按照类型注入
     *      type: 指定类型注入
     *      name: 指定名称注入
     */
//    @Autowired(required = true)
//    @Qualifier("autowiredService")
//    private AutowiredService auto;
//    private AutowiredService autowiredService;
    @Resource(name = "autowiredService")//等价于@Autowired(required = true)+@Qualifier("autowiredService")
    private AutowiredService auto;

    @Override
    public void add() {
//        System.out.println("JZServiceImpl的add方法被调用");
        auto.add();
    }
}
    //springIOC注解开发测例
    @Test
    public void Autowired注解() {
        ApplicationContext c = new ClassPathXmlApplicationContext("springIOCzhujie.xml");
        ZJService zjService = c.getBean("ZJService", ZJService.class);
        zjService.add();
    }

1.3.4 作用范围(单例多例)和生命周期相关注解

@Scope: 相当于bean标签的scope属性
	作用:用于调整bean的作用范围
	使用位置: 被创建的类上
	属性:
		value:指定作用范围的取值。取值是固定的5个,和XML的配置取值是一样的。
			singleton: 单实例 默认值
			prototype: 多实例
@PostConstruct : 使用在方法上
	使用位置: 初始化的方法上
	​作用:指定初始化方法,相当于init-method
@PreDestroy : 使用在方法上
	​作用:指定销毁方法,相当于destroy-method

 测例:

@Service("ZJService")
//@Scope("singleton")//指定当前类为单例模式,等价于scope=“singleton”
@Scope("prototype")//指定当前类为单例模式,等价于scope=“singleton”
@ToString
public class JZServiceImpl implements ZJService {

    public JZServiceImpl() {
        System.out.println("JZServiceImpl无参构造被调用");
    }

    @PostConstruct//等价于init-method
    public void init() {
        System.out.println("JZServiceImpl的初始化方法被调用");
    }

    @PreDestroy//等价于destroy-method
    public void destroy() {
        System.out.println("JZServiceImpl的销毁方法被调用");
    }
}
     @Test
    public void 单例_生命周期注解() {
        //饿汉模式 调用初始化
        ApplicationContext c = new ClassPathXmlApplicationContext("springIOCzhujie.xml");
        ZJService zjService1 = c.getBean("ZJService", ZJService.class);
        ZJService zjService2 = c.getBean("ZJService", ZJService.class);
        //判断单例、多例
        System.out.println(zjService1==zjService2);//true 单例 hash相同 false多例
        //转换 调用close方法 调用销毁
        ClassPathXmlApplicationContext c2= (ClassPathXmlApplicationContext) c;
        c2.close();
    }

1.4、纯注解格式

我们需要使用配置类代替配置文件,将以下注解写在配置类上

@Configuration: 声明该类为Spring的配置文件类
@ComponentScan(basePackages = "hhyZhuJie") : 指定要扫描的包
@PropertySource(value = "classpath:springJZ.properties") : 将配置文件交给Spring容器管理
@Bean() : 将方法返回的对象存放到IOC容器中
	将返回值存放到IOC容器中
	一般将【第三方提供的类】对加载到IOC容器

 测试:准备配置类

@Configuration//等价于xml文件,同时是@Component注解的衍生注解,声明该类为Spring的配置文件类
@ComponentScan(basePackages = "hhyZhuJie")//指定要扫描的包
@PropertySource(value = "classpath:springJZ.properties")//将配置文件交给Spring容器管理
public class SpringZJ {
    //此时读配置文件的代码不能使用new ClassPathXmlApplicationContext,改用AnnotationConfigApplicationContext
}
    //springIOC纯注解开发测例
    @Test
    public void 纯注解() {
        //参数为配置类
        ApplicationContext c = new AnnotationConfigApplicationContext(SpringZJ.class);
        ZJService zjService1 = c.getBean("ZJService", ZJService.class);
        ZJService zjService2 = c.getBean("ZJService", ZJService.class);
        //判断单例、多例
        System.out.println(zjService1==zjService2);//true 单例 hash相同 false多例
        //转换 调用close方法 调用销毁
        AnnotationConfigApplicationContext c2= (AnnotationConfigApplicationContext) c;
        c2.close();
    }

二、整合Junit

作用: 简化单元测试代码

junit 4.12以上版本

步骤:不用创建实现类 直接注入

1.导入jar包坐标
	<!-- 引入单元测试的jar包 4.12以上 -->
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.12</version>
    </dependency>
    <!-- 导入Spring整合junit的jar包 -->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-test</artifactId>
        <version>5.0.2.RELEASE</version>
    </dependency>
2.使用
	在测试类上添加以下两个注解:
	@RunWith(SpringJUnit4ClassRunner.class)
    二选一:
	    @ContextConfiguration(locations="classpath:applicationContext.xml") (配置xml文件的路径)
	    @ContextConfiguration(classes=SpringConfig.class) (配置类)
@RunWith(SpringJUnit4ClassRunner.class)//声明spring提供的单元测试环境
@ContextConfiguration(classes= SpringZJConfig.class) //声明spring的配置信息(配置类)
public class SpringJunit {

    @Autowired
    private ZJService zjService;

    @Test
    public void spring整合Junit() {
        //不用new实现类
//        new JZServiceImpl().add();
        zjService.add();
    }
}

三、回顾动态代理

方法增强: 在【不改变源码】的基础上对方法进行增强
    继承
    装饰模式
    动态代理
AOP: 在不改变源码的基础上对方法进行增强
	底层动态代理:
		proxy: jdk提供的,基于接口
        cglib: 基于子类,生成被代理类的子类

 

 

3.1、基于接口的动态代理(JDK):

作用: 方法增强

要求: 被代理类至少实现一个接口

public interface Car {
    void color();
    void run();
    void jiayou();
}

public class QQ implements Car {
    @Override
    public void color() {
        System.out.println("红色");
    }

    @Override
    public void run() {
        System.out.println("正在跑");
    }

    @Override
    public void jiayou() {
        System.out.println("加油中");
    }
}

public class JdkProxy {
    public static void main(String[] args) {
        //1.定义目标对象 被代理对象 final代理过程中不能被改变
        final QQ qq = new QQ();
        Car car = (Car) Proxy.newProxyInstance(
                qq.getClass().getClassLoader(), //定义类加载器
                qq.getClass().getInterfaces(),//获取布标对象实现接口的字节码对象数组
                new InvocationHandler() {
                    /**
                     * 每次调用代理类的方法时,此方法都会执行
                     * @param proxy : 生成的代理类对象(开发慎用)
                     * @param method : 当前所执行的方法的字节码对象
                     * @param args : 当前执行的方法所传递的参数
                     * @return
                     * @throws Throwable
                     */
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        String name = method.getName();
                        System.out.println("当前执行的方法名称: " + name);
                        if ("run".equals(name)) {
                            System.out.println("本身方法执行前搞点事~~~");
                            //调用本身的方法,前后再增强
                            Object result = method.invoke(qq, args);
                            System.out.println("本身方法执行后搞点事~~~");
                            System.out.println("QQ车以每秒1千米的速度在飞奔...");
                            return null;
                        }
                        return null;
                    }
                }
        );
//        car.color();
        car.run();
    }
}

3.2、无对象的动态代理(JDK):

public interface Car {
    void color();
    void run();
    void jiayou();
}

public class QQ implements Car {
    @Override
    public void color() {
        System.out.println("红色");
    }

    @Override
    public void run() {
        System.out.println("正在跑");
    }

    @Override
    public void jiayou() {
        System.out.println("加油中");
    }
}

public class NoTargetJdkProxy {
    public static void main(String[] args) {
//        final QQ qq = new QQ();
        //强转为被代理类
        Car car = (Car) Proxy.newProxyInstance(
                Car.class.getClassLoader(),//定义类加载器
//                NoTargetJdkProxy.class.getClassLoader(),//定义类加载器,当前类的居然也可以 有待考究
//                qq.getClass().getInterfaces(),//被代理类实现的接口字节码数组(代理类需要实现这些接口)
                new Class[]{Car.class},//自己定义接口的字节码对象数组,不用传入对象,等同于qq.getClass().getInterfaces()
                new InvocationHandler() {
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        final String name = method.getName();
                        if ("color".equals(name)) {
                            System.out.println("color 运行");
                        }else if ("run".equals(name)) {
                            System.out.println("本身方法执行前搞点事~~~");
                            //调用本身的方法,前后再增强
                            Object result = method.invoke(new QQ(), args);
                            System.out.println("本身方法执行后搞点事~~~");
                            System.out.println("QQ车以每秒1千米的速度在飞奔...");
                        }else if ("jiayou".equals(name)) {
                            System.out.println("jiayou 运行");
                        }
                        return null;
                    }
                }
        );
        car.run();
    }
}

3.3、基于子类的动态代理(cglib第三方):

  导入cglib的jar包,但cglib已经被Spring整合了,所以导入Spring-context包即可

    <!--基于子类的动态代理(cglib第三方)-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.0.2.RELEASE</version>
        </dependency>

 无需接口,直接对方法增强大。

//动态为目标生成子类,来达到方法增强的目的
public class UserService {
    public void add(){
        System.out.println("UserService的add方法被调用");
//        int a = 1/0;//测试异常打印
    }
}


public class CglibTest {
    public static void main(String[] args) {
        //定义目标对象
        UserService userService = new UserService();
        //获取子类代理对象
        UserService proxy = (UserService) Enhancer.create(userService.getClass(), new MethodInterceptor() {
            /**
             * 拦截吃了
             * @param proxy : 生成的代理类对象(被代理类的子类)
             * @param method : 当前执行的方法的字节码对象
             * @param args : 当前执行的方法携带的参数数组
             * @param methodProxy : 当前执行的方法的代理对象(不用管)
             * @return
             * @throws Throwable
             */
            @Override
            public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) {
                Object result = null;
                //本身方法调用
                try {
                    System.out.println("本身方法执行前搞点事~~~");
                    result = method.invoke(userService, args);
                    System.out.println("本身方法执行后搞点事~~~");
                } catch (Exception e) {
                    e.printStackTrace();
                    System.out.println("方法异常搞点事~~~");
                } finally {
                    System.out.println("方法最终搞点事~~~");
                }
                return result;
            }
        });
        proxy.add();
    }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值