Spring学习笔记

优点:

  • 开源免费
  • 非入侵式、轻量级的
  • 支持事务的处理(声明式的事务),对框架整合
  • 控制反转(IOC)、面向切面(AOP)的编程思想

Spring是一个控制反转和面向切面编程的轻量级框架(容器)

IoC解释

传统模式

传统的模式

依赖于程序员来创建和实例化对象,类与类之间高耦合,不方便测试和维护

IOC模式

IOC模式的设计

有了IoC容器后,把创建和查找依赖对象的控制权交给了容器,由容器进行注入组合对象,所以对象与对象之间是松散耦合,这样也方便测试,利于功能复用,更重要的是使得程序的整个体系结构变得非常灵活。进行了主从换位,将主动权由程序员转交到了用户手上

Maven配置

<!-- https://mvnrepository.com/artifact/org.springframework/spring-web -->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-web</artifactId>
    <version>5.3.9</version>
</dependency>

设置Beans

  1. 在resources创建xxx.xml文件的beans目录
<?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">

</beans>
  1. 并用Idea创建上下文引用
  2. 在xml创建bean

    <bean id="hello" class="com.mc10010.pojo.Hello"/> # id作为唯一标识,class是完整路径
  3. 创建并强转对象

    ApplicationContext a =  new ClassPathXmlApplicationContext("beans.xml");
    Hello hello = (Hello) a.getBean("hello");

完整代码

原型类:

package com.mc10010.pojo;

/**
 * @author YuKanJiu
 */
public class Hello {
    public void PrintHello(){
        System.out.println("你好呀,Spring!!!");
    }
}

Beans

<?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 id="hello" class="com.mc10010.pojo.Hello"/>
</beans>

测试类

import com.mc10010.pojo.Hello;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Test {
    public static void main(String[] args) {
     ApplicationContext a =  new ClassPathXmlApplicationContext("beans.xml"); //获取beans配置
     Hello hello = (Hello) a.getBean("hello"); //获取类
     hello.PrintHello();
    }
}

IOC创建对象的方式

  • 默认使用的是无参构造
  • 若使用有参构造,则需要

    package com.mc10010.pojo;
    
    /**
     * @author YuKanJiu
     */
    public class Hello {
        private final String name;
    
        public Hello(String name) {
            this.name = name;
        }
    
        public void PrintHello() {
            System.out.println("你好呀," + name + "Spring!!!");
        }
    }
  1. 索引

    <bean id="hello" class="com.mc10010.pojo.Hello">
        <constructor-arg index="0" value="雨看就"/>
    </bean>
  2. 类型

    <bean id="hello" class="com.mc10010.pojo.Hello">
        <constructor-arg type="java.lang.String" value="雨看就"/>
    </bean>
  3. 参数名

    <bean id="hello" class="com.mc10010.pojo.Hello">
       <constructor-arg name="name" value="雨看就"/>
    </bean>

在配置文件加载的适合,容器中管理的对象就已经初始化了

Spring配置

别名

alias

<bean id="hello" class="com.mc10010.pojo.Hello" >
        <constructor-arg name="name" value="雨看就"/>
</bean>
<alias name="hello" alias="newHello"/>

name

也可以用name取别名

<bean id="hello" class="com.mc10010.pojo.Hello" name="newHello">
     <constructor-arg name="name" value="雨看就"/>
 </bean>

import

用于团队开发,用于多个配置文件合并

<import resource="beans.xml"/>
<import resource="beans2.xml"/>
<import resource="beans3.xml"/>

如果重复,则会选择其中一个

依赖注入

Address.java

public class Address {

    public void setAddress(String address) {
        this.address = address;
    }

    @Override
    public String toString() {
        return "Address{" +
                "address='" + address + '\'' +
                '}';
    }

    String address;
}

Student.java

import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Properties;

public class Students {
    private String name;
    private Address address;
    private String[] books;
    private List<String> hobby;
    private Map<String, String> card;
    private String wife;
    private Properties info;

    public String getName() {
        return name;
    }

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

    public Address getAddress() {
        return address;
    }

    public void setAddress(Address address) {
        this.address = address;
    }

    public String[] getBooks() {
        return books;
    }

    public void setBooks(String[] books) {
        this.books = books;
    }

    public List<String> getHobby() {
        return hobby;
    }

    public void setHobby(List<String> hobby) {
        this.hobby = hobby;
    }

    public Map<String, String> getCard() {
        return card;
    }

    public void setCard(Map<String, String> card) {
        this.card = card;
    }

    public String getWife() {
        return wife;
    }

    public void setWife(String wife) {
        this.wife = wife;
    }

    public Properties getInfo() {
        return info;
    }

    public void setInfo(Properties info) {
        this.info = info;
    }

    @Override
    public String toString() {
        return "Students{" +
                "name='" + name + '\'' +
                ", address=" + address.toString() +
                ", books=" + Arrays.toString(books) +
                ", hobby=" + hobby +
                ", card=" + card +
                ", wife='" + wife + '\'' +
                ", info=" + info +
                '}';
    }
}

beans.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 name="student" class="Students">
        <!--基本类型或者String声明-->
        <property name="name" value="张三"/>

        <!--引用类型声明-->
        <property name="address" ref="address"/>

        <!--数组声明-->
        <property name="books">
            <array>
                <value>西游记</value>
                <value>水浒传</value>
                <value>红楼梦</value>
                <value>三国演义</value>
            </array>
        </property>

        <!--Map声明-->
        <property name="card">
            <map>
                <entry key="身份证" value="1111111"/>
                <entry key="银行卡" value="6666666"/>
            </map>
        </property>

        <!--List声明-->
        <property name="hobby">
            <list>
                <value>打球</value>
                <value>游泳</value>
            </list>
        </property>

        <!--null类型声明-->
        <property name="wife">
            <null/>
        </property>

        <!--properties声明-->
        <property name="info">
            <props>
                <prop key="性别">男</prop>
            </props>
        </property>
        
    </bean>
    <bean id="address" class="Address">
    </bean>
</beans>

Test.java

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Test {
    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
        Students student = (Students) context.getBean("student");
        System.out.println(student);
    }
}

结果

Students{name='张三', address=Address{address='null'}, books=[西游记, 水浒传, 红楼梦, 三国演义], hobby=[打球, 游泳], card={身份证=1111111, 银行卡=6666666}, wife='null', info={性别=男}}

C和P命名空间

c是可以用来指定set参数,p是构造方法的命名空间

Student.java

public class Student {
    private String name;

    public String getName() {
        return name;
    }

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

    }

    public Student(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                '}';
    }
}

Beans.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:p="http://www.springframework.org/schema/p"
       xmlns:c="http://www.springframework.org/schema/c"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
    <!--p命名空间-->
    <bean name="student1" class="Student" p:name="小华"/>
    <!--c命名空间-->
    <bean name="student2" class="Student" c:name="小李"/>

</beans>

测试类

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Test {
    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
        //p命名空间
        Student student = (Student) context.getBean("student1");
        System.out.println(student);
        //c命名空间
        Student student2 = (Student) context.getBean("student2");
        System.out.println(student2);
    }
}
/*
    Student{name='小华'}
    Student{name='小李'}
*/

注意:

  • P和C命名空间必须要在头里引入

Bean的作用域

常用的

ScopeDescription
singleton(默认)将每个 Spring IoC 容器的单个 bean 定义范围限定为单个对象实例。
prototype将单个 bean 定义的作用域限定为任意数量的对象实例。

singleton单例模式,每个类生成的对象都是一个唯一的,也是Spring默认的

singleton 单例模式

原型模式:每次生成的对象都不相同

prototype 原型模式

注解自动注入

bean.xml

<?xml version="1.0" encoding="UTF-8"?>
<!--加上context和schemaLocation-->
<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/>
    <bean id="person" class="Person"/>
    <bean id="cat" class="Cat"/>
    <bean id="dog" class="Dog"/>

</beans>

Person.java

import org.springframework.beans.factory.annotation.Autowired;

public class Person {
    @Autowired
     /*有注解,可以省略set方法*/
    private Cat cat;
    @Autowired
    private Dog dog;
    private String name;



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

    @Override
    public String toString() {
        return "Person{" +
                "cat=" + cat +
                ", dog=" + dog +
                ", name='" + name + '\'' +
                '}';
    }
}

Dog.java

public class Dog {
    @Override
    public String toString() {
        return "Dog{}";
    }
}

Cat.java

public class Cat {
    @Override
    public String toString() {
        return "Cat{}";
    }
}

注意

  • 直接在属性上使用即可,也可在set上使用
  • 使用@Autowired不用编写set方法,前提是自动装配的属性在IOC容器中存在,且符合名字ByName
  • 指定注入id

    //可以直接指定id
    @Qualifier(value = "dog")

使用注解开发

  • 注解

User.java

package com.mc10010.dao;


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

@Component
/*
    相当于
    <bean id="user" class="com.mc10010.dao.User"/>
*/
@Scope("prototype")
/*
 *     作用域,原型模式,相当于
 *     <bean id="user" class="com.mc10010.dao.User" scope="prototype"
 *       prototype可以换成singleton单例模式
 *
 */
public class User {
    @Value("雨看就")
    /*
     * 相当于
     * <property name="name" value="雨看就"/>
     * 并可以直接省略set方法,当然也可以在set方法用Value注入
     */
    private String name;

    @Override
    public String toString() {
        return "User{" +
                "name='" + name + '\'' +
                '}';
    }
}

beans.xml

<?xml version="1.0" encoding="UTF-8"?>
<!--加上context和schemaLocation-->
<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.mc10010"/>


</beans>
  • 衍生注解

    • @Service(service层)
    • @Controller(controller层)
  • 自动装配配置

    - @Autowired : 自动装配通过类型,名字
            //可以直接指定id
            @Qualifier(value = "dog")
    - @nullable : 说明该字段可以为空
    - @Resource : 自动装配通过名字,类型

小结

  • xml与注解

    • xml更加万能,适用于任何场合,维护起来更加方便
    • 注解不是自己类用不了,维护起来相对复杂
  • 最佳实践:

    • xml用来管理bean,注解用来完成属性注入
  • 使用过程中注意:

    • 必须开启注解支持

       <!--一定要加这行,开启注解支持-->
      <context:annotation-config/>
      <!--自动扫描包的位置-->
      <context:component-scan base-package="com.mc10010"/>

使用JavaConfig实现装配

装配设置,相当于beans.xml

package com.mc10010.spring06.configuration;

import com.mc10010.spring06.dao.Student;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;

@Configuration
@ComponentScan("com.mc10010.spring06")
public class Beans {
    @Bean
    public Student student() {
        return new Student();
    }
}

实例类

package com.mc10010.spring06.dao;

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

public class Student {
    @Value("王五")
    private String name;

    public String getName() {
        return name;
    }
}

代理模式

模式结构图

模式结构图

静态代理

角色分析

  • 抽象角色: 使用接口或者抽象类来解决
  • 真实角色: 被代理的角色
  • 代理角色:代理真实的角色、代理真实角色后,一般会有附属操作
  • 客户: 访问代理对象的人

代码步骤

  1. 接口
  2. 真实角色
  3. 代理角色
  4. 客户端访问

好处

  • 可以使真实角色操作更加纯粹,不用关注公共的业务
  • 公共业务交给代理角色,实现了业务的分工
  • 公共业务发生扩展的时候方便集中管理

缺点

  • 一个真实角色会产生一个代理角色,代码里会翻倍,开发效率降低

案例一

  • 接口(抽象角色)

    package com.mc10010;
    
    public interface Rent {
        void rentHouse();
    }
  • 真实角色

    package com.mc10010;
    
    public class Host implements Rent{
        @Override
        public void rentHouse() {
            System.out.println("房东租房子");
        }
    }
  • 代理角色

    package com.mc10010;
    
    public class Proxy implements Rent {
        private final Host host;
    
        public Proxy(Host host) {
            this.host = host;
        }
    
        public void signAContract(){
            System.out.println("签合同");
        }
    
        @Override
        public void rentHouse() {
            System.out.println("租房子");
        }
    }
    
  • 客户访问

    package com.mc10010;
    
    public class User {
    
    
        public static void main(String[] args) {
            Proxy proxy = new Proxy(new Host());
            //租房子
            proxy.rentHouse();
            //额外操作
            proxy.signAContract();
        }
    }

动态代理

动态代理类

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

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);
    }

    @Override
    //处理代理实例,返回结果
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        log(method.getName());
        return method.invoke(target,args);
    }

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

代理接口

package com.mc10010.test02;

public interface Rent {
    void rentHouse();
}

被代理的类

package com.mc10010.test02;

public class Host implements Rent {
    @Override
    public void rentHouse() {
        System.out.println("房东租房子");
    }
}

好处

  • 静态代理的全部好处

    • 可以使真实角色操作更加纯粹,不用关注公共的业务
    • 公共业务交给代理角色,实现了业务的分工
    • 公共业务发生扩展的时候方便集中管理
  • 一个动态代理类代理的是一个接口,一般是对应一类业务
  • 一般动态代理类可以代理多个类,只要是实现了这个接口

AOP思想

AOP思想

AOP实现方式之一

  1. 引入maven

    <!-- https://mvnrepository.com/artifact/org.aspectj/aspectjweaver -->
            <dependency>
                <groupId>org.aspectj</groupId>
                <artifactId>aspectjweaver</artifactId>
                <version>1.9.5</version>
            </dependency>
  2. 定义接口和实现类

    package com.mc10010.service;
    
    public interface UserService {
        void add();
        void delete();
        void update();
        void select();
    
    }
package com.mc10010.service;

public class UserServiceImp implements UserService {
    @Override
    public void add() {
        System.out.println("增加");
    }

    @Override
    public void delete() {
        System.out.println("删除");

    }

    @Override
    public void update() {
        System.out.println("更改");

    }

    @Override
    public void select() {
        System.out.println("查询");

    }
}
  1. 定义横向业务

    package com.mc10010.log;
    
    import org.springframework.aop.AfterReturningAdvice;
    
    import java.lang.reflect.Method;
    
    public class AfterLog implements AfterReturningAdvice {
    
        @Override
        public void afterReturning(Object returnValue, Method method, Object[] objects, Object target) {
            System.out.println(target.getClass().getName() + "的" + method.getName() + "被执行了,返回结果为"+returnValue);
        }
    }
package com.mc10010.log;

import org.springframework.aop.MethodBeforeAdvice;

import java.lang.reflect.Method;

public class Log implements MethodBeforeAdvice {
    @Override
    public void before(Method method, Object[] objects, Object target) {
        System.out.println(target.getClass().getName() + "的" + method.getName() + "被执行了");
    }
}
  1. 定义beans

    <?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:aop="http://www.springframework.org/schema/aop"
           xsi:schemaLocation="http://www.springframework.org/schema/beans
            http://www.springframework.org/schema/beans/spring-beans.xsd
            http://www.springframework.org/schema/aop
            http://www.springframework.org/schema/aop/spring-aop.xsd">
    
        <!--注册bean-->
        <bean id="userService" class="com.mc10010.service.UserServiceImp"/>
        <bean id="log" class="com.mc10010.log.Log"/>
        <bean id="afterLog" class="com.mc10010.log.AfterLog"/>
        <!--方式1 用原生SpringAPI接口-->
        <!--配置aop:需要导入aop的约束-->
        <aop:config>
            <!--切入点-->
            <aop:pointcut id="pointcut" expression="execution(* com.mc10010.service.UserServiceImp.*(..))"/>
            <!--执行环绕增加-->
            <aop:advisor advice-ref="log" pointcut-ref="pointcut"/>
            <aop:advisor advice-ref="afterLog" pointcut-ref="pointcut"/>
    
        </aop:config>
    </beans>
  2. 实现测试类

    import com.mc10010.service.UserService;
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.support.ClassPathXmlApplicationContext;
    
    public class MyTest {
        public static void main(String[] args) {
            ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
            //动态代理的是接口!!!!
            UserService userService = (UserService) applicationContext.getBean("userService");
            userService.add();
        }
    }
    /* 输出结果
    * com.mc10010.service.UserServiceImp的add被执行了
    * 增加
    * com.mc10010.service.UserServiceImp的add被执行了,返回结果为null
    */

AOP实现方式之二

  1. 同上
  2. 同上
  3. 定义切面类和切面方法

    package com.mc10010.diy;
    
    public class Diy {
        public void beforeMethod(){
            System.out.println("====方法前====");
        }
        public void afterMethod(){
            System.out.println("====方法后====");
        }
    }
  4. 定义bean

    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xmlns:aop="http://www.springframework.org/schema/aop"
           xsi:schemaLocation="http://www.springframework.org/schema/beans
            http://www.springframework.org/schema/beans/spring-beans.xsd
            http://www.springframework.org/schema/aop
            http://www.springframework.org/schema/aop/spring-aop.xsd">
    
        <bean id="diy" class="com.mc10010.diy.Diy"/>
        <bean id="userService" class="com.mc10010.service.UserServiceImp"/>
    
        <aop:config>
            <aop:aspect ref="diy">
                <aop:pointcut id="point" expression="execution(* com.mc10010.service.UserServiceImp.*(..))"/>
                <aop:before method="beforeMethod" pointcut-ref="point"/>
                <aop:after method="afterMethod" pointcut-ref="point"/>
            </aop:aspect>
    
        </aop:config>
    </beans>
  5. 同上

    import com.mc10010.service.UserService;
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.support.ClassPathXmlApplicationContext;
    
    public class MyTest {
        public static void main(String[] args) {
            ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
            UserService userService = (UserService) context.getBean("userService");
            userService.add();
        }
    }
    
    /*
     * ====方法前====
     * 增加
     * ====方法后====
     * */
最后修改:2021 年 08 月 15 日
如果觉得我的文章对你有用,请随意赞赏