隐约雷鸣,阴霾天空。

Spring学习记录01

Spring学习记录

这是期末作业,本文内容基于个人理解和官方文档以及网上一些资料,如有错误烦请评论区指出

1. Spring是什么

  • Spring是一个开源免费的框架 , 容器 .
  • Spring是一个轻量级的框架 , 非侵入式的 .
  • 控制反转 IoC , 面向切面 Aop
  • 对事物的支持 , 对框架的支持

2. IoC

  • IoC,中文名字控制反转,在Spring中是一个容器,在我们以前的学习的程序中,对使用者来说,程序的控制创建都是交给程序的,但是控制反转则是由我们自行控制创建对象,把主动权交给了调用者,例如程序需要一个对象的时候,不直接在原来的类或者方法中创建,而是由调用者传入。
  • 实现IoC的方法有很多种,现在学习的Spring使用的是DI,也就是依赖注入,上文中由调用者传入一个对象指的就是依赖注入,而控制反转的指的就是获得依赖对象的方式反转了。

3. 使用Spring

  • 和使用大多数框架一样,我们首先需要导入必要的包,如果使用maven则可以直接写入配置文件:

    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-webmvc</artifactId>
        <version>5.1.10.RELEASE</version>
    </dependency>
  • 编写一个实体类:

    public class Hello {
        private String name;
     
        public String getName() {
            return name;
        }
        public void setName(String name) {
            this.name = name;
        }
     
        public void show(){
            System.out.println("Hello,"+ name );
        }
    }
  • 编写Spring配置文件applicationContext.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"
           xsi:schemaLocation="http://www.springframework.org/schema/beans
            http://www.springframework.org/schema/beans/spring-beans.xsd">
     
        <!--bean就是java对象 , 由Spring创建和管理-->
        <bean id="hello" class="com.kuang.pojo.Hello">
            <property name="name" value="Spring"/>
        </bean>
    </beans>
  • 编写测试类:

    @Test
    public void test(){
        //解析beans.xml文件 , 生成管理相应的Bean对象
        ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
        //getBean : 参数即为spring配置文件中bean的id .
        Hello hello = (Hello) context.getBean("hello");
        hello.show();
    }

    在上面的例子中,对象的创建从我们写的程序变成了Spring容器,这就是控制反转。在Hello类中有set方法,从而可以进行依赖注入。

4. 依赖注入和自动装配

  • 依赖注入

    • 依赖注入有配置文件注入和注解注入两种,配置文件注入中有一下几种方式:

      1. 常量注入
      2. Bean注入
      3. 数组注入
      4. List注入
      5. Map注入
      6. Set注入
      7. Null注入
      8. Properties注入
    • 这其中需要注意的是,在传入值的时候,分为value和ref两种,前者指的是值类型,后者是引用类型,在依赖注入的时候,被注入的对象必须要有set方法,其命名要符合JavaBean规范。
    • 具体注入方式参考官网,在此不表。
  • 自动装配

    在Spring中有三种装配方式,分别是:

    1. 在xml中显式配置;
    2. 在java中显式配置;
    3. 隐式的bean发现机制和自动装配

    在自动化的装配中,我们可以给bean标签加上autowire属性,分别有byName和byType两种,顾名思义,前者是按名字装配,后者是按类型装配。

5. 注解

  • Spring中的注解种类繁多,使用注解可以大幅减小配置文件的数量,降低开发难度。使用注解首先要开启注解支持,还要引入aop的包,再加上context约束:

    <context:annotation-config/>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-aop</artifactId>
        <version>5.0.7.RELEASE</version>
    </dependency>
    <?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>
  • 以下是几种常见的Spring注解:

    1. @Configuration 相当于配置文件中的beans,使用之前要在配置文件中开启扫描

      <context:component-scan base-package="xxx.xxx"/>
    2. @Scope 可以配置作用域
    3. @Value 可以手动给属性注入值
    4. @Controller, @Service, @Repository,@Component 相当于bean标签,在类上使用,使用前要在配置文件中开启扫描

      <context:component-scanbase-package=”XXX”/>

      以上四种注解功能相同,只是为了区分所以名字不同。使用以上四种标签时,默认id为类名首字母转小写。

    5. @Autowired 配置在属性上,实现自动装配
    6. @Bean 相当于Bean标签,在方法上使用,方法的名字就相当于bean标签中的id,其返回值相当于class属性。

6. 代理模式

  • 在学习AOP之前,先要学习代理模式,因为AOP的底层机制就是动态代理。

    代理模式分为两种,一种是动态代理,一种是静态代理。、

    在静态代理中,对象有以下四个:

    1. 抽象角色:使用接口或者抽象类来实现
    2. 真实角色:被代理的角色
    3. 代理角色:代理真实的角色,然后做一些附加操作
    4. 客户:使用代理觉得来做一些事情。

    综上所述,之所以要使用代理模式是因为原来的角色(真实角色),多出了需求,但是因为某些原因不适合更改真实对象的代码时,就可以使用代理对象来代替真实对象完成工作。这样一来,有如下几点好处:

    1. 真实对象的工作是纯粹的,不用关注细枝末节
    2. 可以让业务扩展时变得集中和方便管理。
    3. 实现了业务的分工

    但是相对应的,代理类越多,工作量就越大,开发效率遍低。

  • 动态代理
  • 动态代理有以下几点:

    • 动态代理的角色和静态代理的一样 .
    • 动态代理的代理类是动态生成的 . 静态代理的代理类是我们提前写好的
    • 动态代理分为两类 : 一类是基于接口动态代理 , 一类是基于类的动态代理
  • 举一个例子:

    //一个用户增删改查的接口
    package top.acgnx.service;
    
    public interface UserService {
        public void add();
        public void delect();
        public void update();
        public void query();
    }
    //一个实现类作为真实对象,模拟增删改查操作
    package top.acgnx.service;
    
    public class UserServiceImpl implements UserService{
        public void add() {
            System.out.println("add User");
        }
    
        public void delect() {
            System.out.println("delect User");
        }
    
        public void update() {
            System.out.println("update User");
        }
    
        public void query() {
            System.out.println("query User");
        }
    }

    现在需要对上面的真实对象进行增强,使其具有输出日志的功能。

    import java.lang.reflect.Proxy;
    
    //代理对象
    public class ProxyInvocationHandler implements InvocationHandler {
        Object object;
    
        //传入要被代理的真实对象
        public void setObject(Object object) {
            this.object = object;
        }
    
        //使用newProxyInstance方法创建一个代理类并返回
        public Object getProxy(){
            return Proxy.newProxyInstance(this.getClass().getClassLoader(),object.getClass().getInterfaces(),this);
        }
    
        //执行被调用的方法,可以在这里面对原有的方法进行增强
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            log(method.getName());
            //执行传入的方法
            Object ret = method.invoke(object, args);
            //返回执行的结果
            return ret;
        }
    
        public void log(String s){
            System.out.println("方法" + s + "被执行了");
        }
    }

    上面这个类实现了InvocationHandler接口,传入一个真实对象,就可以使用getProxy()方法得到一个被代理后的对象,代理对象调用真实对象中的方法的时候,就会通过invoke被执行,同时增强原有的方法,并把执行后的结果返回。

    import org.junit.Test;
    import top.acgnx.service.ProxyInvocationHandler;
    import top.acgnx.service.UserService;
    import top.acgnx.service.UserServiceImpl;
    
    public class ProxyText {
        @Test
        public void proxyTest(){
            //创建真实对象
            UserServiceImpl userService = new UserServiceImpl();
    
            //创建代理对象
            ProxyInvocationHandler pih = new ProxyInvocationHandler();
    
            //注入要代理的对象
            pih.setObject(userService);
    
            //得到被代理后的对象并强转类型
            UserService proxy = (UserService) pih.getProxy();
    
            //执行增强后的方法
            proxy.add();
        }
    }

    上述代码可以传入任意一个真实对象,并在执行原有方法前输出方法名。

添加新评论