JavaEE(10)Spring中的注解开发、静态/动态代理

1. 使用注解开发

1. 说明
(1)在Spring4之后,想要使用注解的形式,必须先引入aop包
在这里插入图片描述
(2)在配置文件中,引入context约束

<?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:annotation-config/>
</beans>

2. Bean的实现

(1)Bean注入:@Component

  • 指定注解的扫描包
<?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:annotation-config/>

    <!--指定注解扫描包-->
    <context:component-scan base-package="com.nelws"/>
</beans>
  • 在指定包下编写类,增加注解
@Component  //相当于配置文件中 <bean id="user" class="com.nelws.domain.User"/>
public class User {
    public String name = "老王";
}
  • 测试
    @Test
    public void test01(){
        ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
        User user = context.getBean("user", User.class);
        System.out.println(user.name);   //老王
    }

(2)属性注入:@Value

  • 配置实体类方式1
@Component  //相当于配置文件中 <bean id="user" class="com.nelws.domain.User"/>
public class User {

    @Value("小王")  //相当于配置文件中 <property name="name" value="小王"/>
    public String name;
}
  • 测试1
    @Test
    public void test02(){
        ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
        User user = context.getBean("user", User.class);
        System.out.println(user.name);   //小王
    }
  • 配置实体类方式2
@Component  //相当于配置文件中 <bean id="user" class="com.nelws.domain.User"/>
public class User {

    public String name;

    @Value("王王王")  //相当于配置文件中 <property name="name" value="小王"/>
    public void setName(String name) {
        this.name = name;
    }
}
  • 测试2
    @Test
    public void test03(){
        ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
        User user = context.getBean("user", User.class);
        System.out.println(user.name);   //王王王
    }

(3)@Component的三个衍生注解

为了更好地进行分层,Spring可以使用其他三个注解,功能一样
	@Controller:web层
	@Service:service层
	@Repository:dao层

(4)作用域:@Scope

singleton:默认的,Spring会采用单例模式创建这个对象。关闭工厂,所有的对象都会被销毁
prototype:多例模式。关闭工厂,所有的对象不会被销毁。内部的垃圾回收机制会回收
@Component  //相当于配置文件中 <bean id="user" class="com.nelws.domain.User"/>
@Scope("singleton")
public class User {

    public String name;

    @Value("王王王")  //相当于配置文件中 <property name="name" value="小王"/>
    public void setName(String name) {
        this.name = name;
    }
}

3. 总结
(1)XML与注解比较

XML可以适用任何场景 ,结构清晰,维护方便
注解不是自己提供的类使用不了,开发简单方便

(2)XML与注解整合开发

xml管理Bean
注解完成属性注入
使用过程中, 可以不用扫描,扫描是为了类上的注解

4. 基于Java类进行配置

(1)说明:JavaConfig原来是Spring的一个子项目,它通过Java类的方式提供Bean的定义信息,在 Spring4的版本,JavaConfig已正式成为Spring4的核心功能

(2)测试

  • 实体类Dog
@Component
public class Dog {
    public String name = "旺财";
}
  • 新建一个config配置包,编写一个MyConfig的配置类
@Configuration  //表明这是一个配置类,类似之前的beans.xml,这个也会被Spring容器托管,注入到容器中
@ComponentScan("com.nelws.domain")
public class MyConfig {

    /**
     * 注册一个Bean,就相当于之前写的一个bean标签
     * 这个方法的名字,就相当于bean标签中的id
     * 这个方法的返回值,就相当于bean标签的class
     */
    @Bean
    public Dog dog(){
        return new Dog();  //这个就是返回要注入到bean的对象
    }
}
  • 测试
    @Test
    public void test04(){
        ApplicationContext context = new AnnotationConfigApplicationContext(MyConfig.class);
        Dog dog = context.getBean("dog", Dog.class);
        System.out.println(dog.name);   //旺财
    }

(3)导入其他配置:@Import

@Configuration  //代表这是一个配置类
public class MyConfig2 {
}
@Configuration
@Import(MyConfig2.class)  //导入合并其他配置类,类似于配置文件中的 inculde 标签
public class MyConfig {
 
    @Bean
    public Dog dog(){
        return new Dog();
    }
 
}

2. 静态/动态代理

1. 学习代理模式的原因:Spring的AOP机制的底层就是动态代理
2. 代理模式
在这里插入图片描述
3. 静态代理
(1)静态代理的角色分析

抽象角色:一般使用接口或者抽象类来实现
真实角色:被代理的角色
代理角色:代理真实角色;代理真实角色后,一般会做一些附属操作
客户:使用代理角色来进行操作

(2)测试

  • Rent(抽象角色)
/**
 * 抽象角色:租房
 */
public interface Rent {
    public void rent();
}

  • Host(真实角色)
/**
 * 真实角色:房东
 * 房东要租房子
 */
public class Host implements Rent{
    public void rent() {
        System.out.println("房东要出租房屋!");
    }
}
  • Proxy(代理角色)
/**
 * 代理角色:中介
 */
public class Proxy implements Rent{

    private Host host;

    public Proxy() {
    }

    public Proxy(Host host) {
        this.host = host;
    }

    //租房
    public void rent() {
        seeHouse();
        host.rent();
        fare();
    }

    //看房
    public void seeHouse(){
        System.out.println("带房客看房");
    }

    //收中介费
    public void fare(){
        System.out.println("收中介费");
    }
}
  • Client(客户)
/**
 * 客户:一般客户都会去找代理
 */
public class Client {
    public static void main(String[] args) {
        //房东要租房
        Host host = new Host();
        //中介帮助房东
        Proxy proxy = new Proxy(host);
        //客户去找中介
        proxy.rent();
        /**
         * 带房客看房
         * 房东要出租房屋!
         * 收中介费
         */
         
    }
}

分析:在这个过程中,客户直接接触的就是中介,就如同现实生活中的样子,你看不到房东,但是你依旧租到了房东的房子通过代理,这就是所谓的代理模式

(3)静态代理的优缺点

优点
	可以使得我们的真实角色更加纯粹,不再去关注一些公共的事情
	公共的业务由代理来完成,实现了业务的分工
	公共业务发生扩展时变得更加集中和方便
缺点
	类多了,多了代理类,工作量变大了,开发效率降低

4. 静态代理的深入理解

(1)环境搭建

  • 创建一个抽象角色,比如我们平时做的用户业务,抽象起来就是增删改查
/**
 * 抽象角色:完成增删改查业务
 */
public interface UserService {
    void add();
    void delete();
    void update();
    void query();
}
  • 我们需要一个真实的对象来完成这些增删改查的操作
/**
 * 真实角色:完成增删改查操作的人
 */
public class UserServiceImpl implements UserService{
    public void add() {
        System.out.println("增加一个用户");
    }

    public void delete() {
        System.out.println("删除一个用户");
    }

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

    public void query() {
        System.out.println("查询一个用户");
    }
}
  • 测试
/**
 * 客户
 */
public class Client {
    public static void main(String[] args) {
        UserService service = new UserServiceImpl();
        service.add();   //增加一个用户
        service.delete();   //删除一个用户
    }
}

(2)突然增加了一个需求:用在调用方法时增加一个日志功能。我们要使用代理模式去做,可以再不影响原来业务的情况下,实现此功能

  • 创建一个抽象角色,比如我们平时做的用户业务,抽象起来就是增删改查
/**
 * 抽象角色:完成增删改查业务
 */
public interface UserService {
    void add();
    void delete();
    void update();
    void query();
}
  • 我们需要一个真实的对象来完成这些增删改查的操作
/**
 * 真实角色:完成增删改查操作的人
 */
public class UserServiceImpl implements UserService{
    public void add() {
        System.out.println("增加一个用户");
    }

    public void delete() {
        System.out.println("删除一个用户");
    }

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

    public void query() {
        System.out.println("查询一个用户");
    }
}
  • 现在设置一个代理角色,来完成处理日志的功能
public class UserServiceProxy implements UserService{
    private UserServiceImpl userService;

    public void setUserService(UserServiceImpl userService) {
        this.userService = userService;
    }

    public void add() {
        showLog("add");
        userService.add();
    }

    public void delete() {
        showLog("delete");
        userService.delete();
    }

    public void update() {
        showLog("update");
        userService.update();
    }

    public void query() {
        showLog("query");
        userService.query();
    }

    public void showLog(String log){
        System.out.println("[Debug] 日志~" + log + "~执行了");
    }
}
  • 测试
public class Client {
    public static void main(String[] args) {

        //真实业务
        UserServiceImpl service = new UserServiceImpl();
        //代理类
        UserServiceProxy proxy = new UserServiceProxy();
        //使用代理类提供的日志功能
        proxy.setUserService(service);

        proxy.add();
        proxy.delete();
        /**
         * [Debug] 日志~add~执行了
         * 增加一个用户
         * [Debug] 日志~delete~执行了
         * 删除一个用户
         */

    }
}

结论:我们在不改变原来的代码的情况下,实现了对原有功能的增强,这是AOP中最核心的思想
在这里插入图片描述
5. 动态代理
(1)说明:动态代理的角色和静态代理的角色是一样的,区别是动态代理的代理类是动态生成的,静态代理的代理类是写死的

(2)动态代理的分类

  • 基于接口的动态代理----JDK动态代理
  • 基于类的动态代理----cglib

(3)现在用的比较多的是javassist来生成动态代理(JDK动态代理)

(4)JDK动态代理核心的两个类:InvocationHandler和Proxy

  • InvocationHandler【调用处理程序】
    在这里插入图片描述
Object invoke(Object proxy, Method method, Object[] args)//参数
//proxy - 调用该方法的代理实例
//method -所述方法对应于调用代理实例上的接口方法的实例。方法对象的声明类将是该方法声明的接口,它可以是代理类继承该方法的代理接口的超级接口。
//args -包含的方法调用传递代理实例的参数值的对象的阵列,或null如果接口方法没有参数。原始类型的参数包含在适当的原始包装器类的实例中,例如java.lang.Integer或java.lang.Boolean 。
  • Proxy【代理】
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
//生成代理类
public Object getProxy(){
    return Proxy.newProxyInstance(this.getClass().getClassLoader(),
                                  target.getClass().getInterfaces(),this);
}

(5)测试

  • Rent(抽象角色)
/**
 * 抽象角色:租房
 */
public interface Rent {
    public void rent();
}
  • Host(真实角色)
/**
 * 真实角色:房东
 */
public class Host implements Rent{
    public void rent() {
        System.out.println("房东要租房子!");
    }
}

  • ProxyInvocationHandler(代理角色)
/**
 * 代理角色:中介
 * 我们就用这个类,自动生成代理类
 */
public class ProxyInvocationHandler implements InvocationHandler {

    private Rent rent;

    public void setRent(Rent rent) {
        this.rent = rent;
    }

    //生成代理类,重点是第二个参数,获取要代理的抽象角色
    //之前都是一个角色,现在可以代理一类角色
    public Object getProxy(){
        return Proxy.newProxyInstance(this.getClass().getClassLoader(),rent.getClass().getInterfaces(),this);
    }

    //proxy:代理类
    //method:代理类的调用处理程序的方法对象
    //处理代理示例上的方法调用并返回结果
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        seeHouse();
        //核心:利用反射实现
        Object result = method.invoke(rent, args);
        fare();
        return result;
    }

    public void seeHouse(){
        System.out.println("带房客看房");
    }

    public void fare(){
        System.out.println("收中介费");
    }
}
  • Client(客户)
public class Client {
    public static void main(String[] args) {
        //真实角色
        Host host = new Host();
        //代理实例的调用
        ProxyInvocationHandler pih = new ProxyInvocationHandler();
        pih.setRent(host);  //将真实角色放进去
        Rent proxy = (Rent) pih.getProxy();
        proxy.rent();
        /**
         * 带房客看房
         * 房东要租房子!
         * 收中介费
         */
    }
}

核心:一个动态代理,一般代理某一类业务,一个动态代理可以代理多个类,代理的是接口!

6. 动态代理深化理解
(1)使用动态代理来实现之前的UserService
(2)测试

  • UserService(抽象角色)
/**
 * 抽象角色:完成增删改查业务
 */
public interface UserService {
    void add();
    void delete();
    void update();
    void query();
}
  • UserServiceImpl(真实角色)
/**
 * 真实角色:完成增删改查操作的人
 */
public class UserServiceImpl implements UserService {
    public void add() {
        System.out.println("增加一个用户");
    }

    public void delete() {
        System.out.println("删除一个用户");
    }

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

    public void query() {
        System.out.println("查询一个用户");
    }
}
  • ProxyInvocationHandler(代理类):我们可以编写一个通用的代理类
public class ProxyInvocatinHandler implements InvocationHandler {

    private Object target;

    public void setObject(Object object) {
        this.target = object;
    }

    //生成代理的类
    public Object getProxy(){
        return Proxy.newProxyInstance(this.getClass().getClassLoader(),target.getClass().getInterfaces(),this);
    }


    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        getLog(method.getName());
        Object result = method.invoke(target, args);
        return result;
    }

    public void getLog(String methodName){
        System.out.println("[Debug] 执行了~" + methodName + "~方法");
    }
}
  • Client(客户)
public class Client {
    public static void main(String[] args) {
        //真实对象
        UserServiceImpl userService = new UserServiceImpl();
        //代理对象的调用处理程序
        ProxyInvocatinHandler pih = new ProxyInvocatinHandler();
        pih.setObject(userService);  //设置代理对象
        UserService proxy = (UserService) pih.getProxy();   //生成动态代理类
        proxy.add();
        proxy.delete();
        /**
         * [Debug] 执行了~add~方法
         * 增加一个用户
         * [Debug] 执行了~delete~方法
         * 删除一个用户
         */
    }
}

7. 总结:动态代理的好处
(1)可以使真实角色的操作更加纯粹,不用去关注一些公共业务
(2)公共业务交给代理角色,实现了业务的分工
(3)公共业务发生扩展的时候,方便集中管理
(4)一个动态代理类代理一个接口,一般就是对应的一个业务
(5)一个动态代理类可以代理多个类,只要实现了同一个接口即可

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值