Spring学习(三)

Spring学习(三)

注解方式

使用

在spring4之后,想要使用注解形式,必须得要引入aop的包

在配置文件当中,还得要引入一个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">

</beans>

Bean的实现

我们之前都是使用 bean 的标签进行bean注入,但是实际开发中,我们一般都会使用注解!

  1. 配置扫描哪些包下的注解
<!--指定注解扫描包-->
<context:component-scan base-package="com.kuang.pojo"/>
  1. 在指定包下编写类,增加注解
@Component("user")
// 相当于配置文件中 <bean id="user" class="当前注解的类"/>
public class User {
   public String name = "1LDK";
}

3、测试

@Test
public void test(){
   ApplicationContext applicationContext =
       new ClassPathXmlApplicationContext("beans.xml");
   User user = (User) applicationContext.getBean("user");
   System.out.println(user.name);
}

属性注入

使用注解注入属性

1、可以不用提供set方法,直接在直接名上添加@value("值")

@Component("user")
// 相当于配置文件中 <bean id="user" class="当前注解的类"/>
public class User {
   @Value("1LDK")
   // 相当于配置文件中 <property name="name" value="1LDK"/>
   public String name;
}

2、如果提供了set方法,在set方法上添加@value("值");

@Component("user")
public class User {

   public String name;

   @Value("1LDK")
   public void setName(String name) {
       this.name = name;
  }
}

衍生注解

我们这些注解,就是替代了在配置文件当中配置步骤而已!更加的方便快捷!

@Component三个衍生注解

为了更好的进行分层,Spring可以使用其它三个注解,功能一样,目前使用哪一个功能都一样。

  • @Controller:web层
  • @Service:service层
  • @Repository:dao层

写上这些注解,就相当于将这个类交给Spring管理装配了!

自动装配注解

@AutoWired

autowired默认是bytype的。如果唯一则注入,否则byName查找。

使用qualifier指定bean的name 达到byname的用法

@Qualifier("指定name")

作用域

@scope

  • singleton:默认的,Spring会采用单例模式创建这个对象。关闭工厂,所有的对象都会销毁。
  • prototype:多例模式。关闭工厂 ,所有的对象不会销毁。内部的垃圾回收机制会回收
@Controller("user")
@Scope("prototype")
public class User {
   @Value("1LDK")
   public String name;
}

小结

XML与注解比较

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

xml与注解整合开发 :推荐最佳实践

  • xml管理Bean
  • 注解完成属性注入
  • 使用过程中, 可以不用扫描,扫描是为了类上的注解
<context:annotation-config/>  

作用:

  • 进行注解驱动注册,从而使注解生效

  • 用于激活那些已经在spring容器里注册过的bean上面的注解,也就是显示的向Spring注册

  • 如果不扫描包,就需要手动配置bean

  • 如果不加注解驱动,则注入的值为null!

基于Java类进行配置

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

测试:

1、编写一个实体类,Dog

@Component  //将这个类标注为Spring的一个组件,放到容器中!
public class Dog {
   public String name = "dog";
}

2、新建一个config配置包,编写一个MyConfig配置类

@Configuration  //代表这是一个配置类
public class MyConfig {

   @Bean //通过方法注册一个bean,这里的返回值就Bean的类型,方法名就是bean的id!
   public Dog dog(){
       return new Dog();
  }

}

3、测试

@Test
public void test2(){
   ApplicationContext applicationContext =
           new AnnotationConfigApplicationContext(MyConfig.class);
   Dog dog = (Dog) applicationContext.getBean("dog");
   System.out.println(dog.name);
}

4、成功输出结果!

导入其他配置如何做呢?

1、我们再编写一个配置类!

@Configuration  //代表这是一个配置类
public class MyConfig2 {
}

2、在之前的配置类中我们来选择导入这个配置类

@Configuration
@Import(MyConfig2.class)  //导入合并其他配置类,类似于配置文件中的 inculde 标签
public class MyConfig {

   @Bean
   public Dog dog(){
       return new Dog();
  }

}

关于这种Java类的配置方式,我们在之后的SpringBoot 和 SpringCloud中还会大量看到,我们需要知道这些注解的作用即可!

代理模式

AOP的底层机制就是动态代理!

代理模式:

  • 静态代理

  • 动态代理

静态代理

静态代理角色分析

  • 抽象角色 : 一般使用接口或者抽象类来实现

  • 真实角色 : 被代理的角色

  • 代理角色 : 代理真实角色 ; 代理真实角色后 , 一般会做一些附属的操作

  • 客户 : 使用代理角色来进行一些操作

代码实现

Rent.java 即抽象角色

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

Host.java 即真实角色

//真实角色:房东,房东要出租房子
public class Host implements Rent{
   public void rent() {
       System.out.println("房屋出租");
  }
}

Proxy.java 即代理角色

//代理角色:中介
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.java 即客户

//客户类,一般客户都会去找代理!
public class Client {
   public static void main(String[] args) {
       //房东要租房
       Host host = new Host();
       //中介帮助房东
       Proxy proxy = new Proxy(host);

       //你去找中介!
       proxy.rent();
  }
}

分析:在这个过程中,你直接接触的就是中介,就如同现实生活中的样子,你看不到房东,但是你依旧租到了房东的房子通过代理,这就是所谓的代理模式,程序源自于生活,所以学编程的人,一般能够更加抽象的看待生活中发生的事情。

静态代理的好处:

  • 可以使得我们的真实角色更加纯粹,不再去关注一些公共的事情。
  • 公共的业务由代理来完成,实现了业务的分工。
  • 公共业务发生扩展时变得更加集中和方便。

缺点 :

  • 类多了 , 多了代理类 , 工作量变大了,开发效率降低。

我们想要静态代理的好处,又不想要静态代理的缺点,所以 , 就有了动态代理 !

静态代理加深理解

  1. 创建一个抽象角色,比如平时做的用户业务,抽象起来就是增删改查!
//抽象角色:增删改查业务
public interface UserService {
   void add();
   void delete();
   void update();
   void query();
}
  1. 我们需要一个真实对象来完成这些增删改查操作
//真实对象,完成增删改查操作的人
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("查询了一个用户");
  }
}
  1. 需求来了,现在我们需要增加一个日志功能,怎么实现!
  • 思路1:在实现类上增加代码 【麻烦!】
  • 思路2:使用代理来做,能够不改变原来的业务情况下,实现此功能就是最好的了!
  1. 设置一个代理类来处理日志!代理角色
//代理角色,在这里面增加日志的实现
public class UserServiceProxy implements UserService {
   private UserServiceImpl userService;

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

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

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

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

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

   public void log(String msg){
       System.out.println("执行了"+msg+"方法");
  }

}

5、测试访问类:

public class Client {
   public static void main(String[] args) {
       //真实业务
       UserServiceImpl userService = new UserServiceImpl();
       //代理类
       UserServiceProxy proxy = new UserServiceProxy();
       //使用代理类实现日志功能!
       proxy.setUserService(userService);

       proxy.add();
  }
}

AOP其中的思想:

我们在不改变原来的代码的情况下,实现了对原有功能的增强,这是AOP中最核心的思想。即横向开发,重点是在同一层级下增强功能。

  • 动态代理的角色和静态代理的一样

  • 动态代理的代理类是动态生成的 . 静态代理的代理类是我们提前写好的

  • 动态代理分为两类 : 一类是基于接口动态代理 , 一类是基于类的动态代理

    • 基于接口的动态代理----JDK动态代理
    • 基于类的动态代理--cglib
    • 现在用的比较多的是 javasist 来生成动态代理 . 百度一下javasist
    • 我们这里使用JDK的原生代码来实现,其余的道理都是一样的!

JDK的动态代理需要了解两个类

核心 : InvocationHandler和Proxy

【InvocationHandler:调用处理程序】

Object invoke(Object proxy, 方法 method, Object[] args);
//参数
//proxy - 调用该方法的代理实例
//method -所述方法对应于调用代理实例上的接口方法的实例。方法对象的声明类将是该方法声明的接口,它可以是代理类继承该方法的代理接口的超级接口。
//args -包含的方法调用传递代理实例的参数值的对象的阵列,或null如果接口方法没有参数。原始类型的参数包含在适当的原始包装器类的实例中,例如java.lang.Integer或java.lang.Boolean 。

【Proxy:代理】

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

代码实现

抽象角色和真实角色和之前的一样!

Rent. java 即抽象角色

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

Host. java 即真实角色

//真实角色: 房东,房东要出租房子
public class Host implements Rent{
   public void rent() {
       System.out.println("房屋出租");
  }
}

ProxyInvocationHandler. java 即代理角色

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 : 代理类的调用处理程序的方法对象.
   // 处理代理实例上的方法调用并返回结果
   @Override
   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. java

//租客
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();
  }

}

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

深化理解

我们来使用动态代理实现代理我们后面写的UserService!

我们也可以编写一个通用的动态代理实现的类!所有的代理对象设置为Object即可!

public class ProxyInvocationHandler implements InvocationHandler {
   private Object target;

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

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

   // proxy : 代理类
   // method : 代理类的调用处理程序的方法对象.
   public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
       log(method.getName());
       Object result = method.invoke(target, args);
       return result;
  }

   public void log(String methodName){
       System.out.println("执行了"+methodName+"方法");
  }

}
public class Test {
   public static void main(String[] args) {
       //真实对象
       UserServiceImpl userService = new UserServiceImpl();
       //代理对象的调用处理程序
       ProxyInvocationHandler pih = new ProxyInvocationHandler();
       pih.setTarget(userService); //设置要代理的对象
       UserService proxy = (UserService)pih.getProxy(); //动态生成代理类!
       proxy.delete();
  }
}

动态代理的好处

静态代理有的它都有,静态代理没有的,它也有!

  • 可以使得我们的真实角色更加纯粹 . 不再去关注一些公共的事情 .
  • 公共的业务由代理来完成 . 实现了业务的分工 ,
  • 公共业务发生扩展时变得更加集中和方便 .
  • 一个动态代理 , 一般代理某一类业务
  • 一个动态代理可以代理多个类,代理的是接口!
posted @ 2021-04-04 20:55  1LDK  阅读(65)  评论(0编辑  收藏  举报