【2020-06-22】Spring学习笔记Part-2

Jammm
2020-06-22 / 0 评论 / 493 阅读 / 正在检测是否收录...

Powered By JamPang

QQ:847885907

https://jampang.cn/

Spring笔记


其他部分访问

第一部分:点击访问
第三部分:点击访问


6、依赖注入

项目名:spring-04-di

  • 依赖注入(Dependency Injection,DI)。
  • 依赖 : 指Bean对象的创建依赖于容器,Bean对象的依赖资源。
  • 注入 : 指Bean对象所依赖的资源,由容器来设置和装配。

6.1、构造器注入

之前说过,参考第四点。

<bean id="user" class="com.jam.pojo.User">
    <constructor-arg name="name" value="jam"/>
</bean>

6.2、Set方式注入【重点】

  • 依赖注入:Set注入!

    • 依赖:bean对象的创建依赖于容器!
    • 注入:bean对象中的所有属性,由容器来注入!

【环境搭建】

  1. 复杂类型 Address.java

    public class Address {
        private String address;
    
        public String getAddress() {
            return address;
        }
    
        public void setAddress(String address) {
            this.address = address;
        }
    }
  1. 真实测试对象 Student.java

    public class Student {
    
        private String name;
        private Address address;
        private String[] books;
        private List<String> hobbys;
        private Map<String,String> card;
        private Set<String> games;
        private Properties info;
        private String wife;
    
        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> getHobbys() {
            return hobbys;
        }
    
        public void setHobbys(List<String> hobbys) {
            this.hobbys = hobbys;
        }
    
        public Map<String, String> getCard() {
            return card;
        }
    
        public void setCard(Map<String, String> card) {
            this.card = card;
        }
    
        public Set<String> getGames() {
            return games;
        }
    
        public void setGames(Set<String> games) {
            this.games = games;
        }
    
        public Properties getInfo() {
            return info;
        }
    
        public void setInfo(Properties info) {
            this.info = info;
        }
    
        public String getWife() {
            return wife;
        }
    
        public void setWife(String wife) {
            this.wife = wife;
        }
    
        @Override
        public String toString() {
            return "Student{" +
                    "name='" + name + '\'' +
                    ", address=" + address +
                    ", books=" + Arrays.toString(books) +
                    ", hobbys=" + hobbys +
                    ", card=" + card +
                    ", games=" + games +
                    ", info=" + info +
                    ", wife='" + wife + '\'' +
                    '}';
        }
    }
  1. 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
            https://www.springframework.org/schema/beans/spring-beans.xsd">
    
        <bean id="student" class="com.jam.pojo.Student">
            <!--第一种,普通值注入,value-->
            <property name="name" value="Jam"/>
        </bean>
    
    </beans>
  1. 测试类

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

6.2.1完善注入信息[value/ref/array/list/map/set/null/properties]

<?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
        https://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="address" class="com.jam.pojo.Address">
        <property name="address" value="江西"/>
    </bean>


    <bean id="student" class="com.jam.pojo.Student">

        <!--第一种,普通值注入,value-->
        <property name="name" value="Jam"/>

        <!--第二种,Bean注入,ref-->
        <property name="address" ref="address"/>

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

        <!-- List -->
        <property name="hobbys">
            <list>
                <value>听歌</value>
                <value>敲代码</value>
                <value>看电影</value>
            </list>
        </property>

        <!-- Map -->
        <property name="card">
            <map>
                <entry key="身份证" value="111111222222223333"/>
                <entry key="银行卡" value="6222021505001111111"/>
            </map>
        </property>

        <!-- Set -->
        <property name="games">
            <set>
                <value>LOL-英雄联盟</value>
                <value>COC-部落冲突</value>
                <value>BOB-球球大作战</value>
            </set>
        </property>

        <!-- null -->
        <property name="wife">
            <null/>
        </property>

        <!-- Properties -->
        <property name="info">
            <props>
                <prop key="学号">20200527</prop>
                <prop key="性别">男</prop>
                <prop key="姓名">旧梦</prop>
            </props>
        </property>

    </bean>

</beans>

6.3、拓展方式注入

我们可以使用p命名空间和c命名空间进行注入

p命名包

xmlns:p="http://www.springframework.org/schema/p"

c命名包

xmlns:c="http://www.springframework.org/schema/c"

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
        https://www.springframework.org/schema/beans/spring-beans.xsd">

    <!-- p命名空间注入,可以直接注入属性的值:property -->
    <bean id="user" class="com.jam.pojo.User" p:name="Jam" p:age="18"/>

    <!-- c命名空间注入,通过构造器注入:construct-args -->
    <bean id="user2" class="com.jam.pojo.User" c:name="Tim" c:age="18"/>

</beans>

测试

@Test
    public void test2(){
        ApplicationContext context = new ClassPathXmlApplicationContext("userbeans.xml");
        User user = context.getBean("user",User.class);
        System.out.println(user);
    }

    @Test
    public void test3(){
        ApplicationContext context = new ClassPathXmlApplicationContext("userbeans.xml");
        User user = context.getBean("user2",User.class);
        System.out.println(user);
    }

注意:p命名和c命名空间不能直接使用,需要导入xml约束!

xmlns:p="http://www.springframework.org/schema/p"
xmlns:c="http://www.springframework.org/schema/c"

6.4、Bean的作用域

ScopeDescription
singleton(Default) Scopes a single bean definition to a single object instance for each Spring IoC container.
prototypeScopes a single bean definition to any number of object instances.
requestScopes a single bean definition to the lifecycle of a single HTTP request. That is, each HTTP request has its own instance of a bean created off the back of a single bean definition. Only valid in the context of a web-aware Spring ApplicationContext.
sessionScopes a single bean definition to the lifecycle of an HTTP Session. Only valid in the context of a web-aware Spring ApplicationContext.
applicationScopes a single bean definition to the lifecycle of a ServletContext. Only valid in the context of a web-aware Spring ApplicationContext.
websocketScopes a single bean definition to the lifecycle of a WebSocket. Only valid in the context of a web-aware Spring ApplicationContext.
  1. 单例模式(Spring默认机制)

    bean id="user2" class="com.jam.pojo.User" c:name="Tim" c:age="18" scope="singleton"/>
  2. 原型模式:每次从容器中get的时候,都会产生一个新的对象!

    bean id="user2" class="com.jam.pojo.User" c:name="Tim" c:age="18" scope="prototype"/>
  3. 其余的request、session、application这些只能在web开发中使用到!

7、Bean的自动装配

项目名:spring-05-Autowired

  • 自动装配是Spring满足bean依赖的一种方式。
  • Spring会在上下文中自动寻找,并自动给bean装配属性!

在Spring中有三种装配的方式

  1. 在xml中显示的配置
  2. 在java中显示配置
  3. 隐式的自动装配【重点】

7.1、测试

环境搭建:一个人有两个宠物!

Cat.java

public class Cat {

    public void shout(){
        System.out.println("miao");
    }

}

Dog.java

public class Dog {

    public void shout(){
        System.out.println("wang");
    }

}

People.java

public class People {

    private Cat cat;
    private Dog dog;
    private String name;

    public Cat getCat() {
        return cat;
    }

    public void setCat(Cat cat) {
        this.cat = cat;
    }

    public Dog getDog() {
        return dog;
    }

    public void setDog(Dog dog) {
        this.dog = dog;
    }

    public String getName() {
        return name;
    }

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

    @Override
    public String toString() {
        return "People{" +
                "cat=" + cat +
                ", dog=" + dog +
                ", 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"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="cat" class="com.jam.pojo.Cat"/>
    <bean id="dog" class="com.jam.pojo.Dog"/>

    <bean id="people" class="com.jam.pojo.People">
        <property name="name" value="Jam"/>
        <property name="cat" ref="cat"/>
        <property name="dog" ref="dog"/>
    </bean>

</beans>

Test.java

@Test
public void test1(){
    ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
    People people = context.getBean("people", People.class);
    people.getDog().shout();
    people.getCat().shout();
}

7.2、ByName自动装配

bean.xml

<!--
     byName:会自动在容器上下文中查找和自己对象set方法后面的值对应的beanid
-->
<bean id="cat" class="com.jam.pojo.Cat"/>
<bean id="dog" class="com.jam.pojo.Dog"/>

<bean id="people" class="com.jam.pojo.People" autowire="byName">
    <property name="name" value="Jam"/>
</bean>

7.3、ByType自动装配

<!--
     byType:会自动在容器上下文中查找和自己对象属性类型相同的bean
-->
<bean class="com.jam.pojo.Cat"/>
<bean class="com.jam.pojo.Dog"/>

<bean id="people" class="com.jam.pojo.People" autowire="byType">
    <property name="name" value="Jam"/>
</bean>

7.4、byName/byType小结

  • byName的时候,需要保证所有bean的id唯一,并且这个bean需要和自动注入的属性的set方法的值一致
  • byType的时候,需要保证所有bean的class唯一,并且这个bean需要和自动注入的属性的类型一致

7.5、使用注解实现自动装配

jdk1.5支持的注解,spring从2.5支持注解。

The introduction of annotation-based configuration raised the question of whether this approach is “better” than XML. The short answer is “it depends.”

要使用注解须知:

  1. 导入约束 context约束
  2. 配置注解的支持

    <?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
            https://www.springframework.org/schema/beans/spring-beans.xsd
            http://www.springframework.org/schema/context
            https://www.springframework.org/schema/context/spring-context.xsd">
    
        <context:annotation-config/>
    
    </beans>

@Autowired

直接在属性上使用即可,也可以在Set方式上使用!

使用Autowired我们可以不用编写Set方法了,前提是你的自动装配的属性在IOC(Spring)容器存在,且符合名字byname!

科普:

@Nullable  字段标记了这个注解,说明这个字段可以为null
public @interface AutoWired{
    boolean required() default true;
}

测试代码

//如果显示定义了Autowired的required属性为false,说明这个对象可以为null,否则不允许为空
@Autowired(required = false)
private Cat cat;
@Autowired
private Dog dog;
private String name;

如果@Autowired自动装配的环境比较复杂,自动装配无法通过一个注解【@Autowired】完成的时候,我们可以使用@Qulifier(value="XXX")去配置@Autowired的使用,指定一个唯一的bean对象注入!

@Autowired
@Qulifier(value="cat111")
private Cat cat;
@Autowired
@Qulifier(value="222")
private Dog dog;
private String name;

@Resource注解

public class People(){
    
    @Resource(name="cat2")
    private Cat cat;
    
    @Resource
    private Dog dog;
}

7.6、注解小结

@Resource和@Autowired的区别:

  • 都是用来自动装配的,都可以放在属性字段上。
  • @Autowired通过byType的方式现实
  • @Resource默认通过byName的方式实现,如果找不到名字,则通过byType实现。如果两个都找不到的情况下就报错。【常用】
  • 执行顺序不同:@Autowired通过byType的方式现实,@Resource默认通过byName的方式实现

8、使用注解开发

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

    <context:annotation-config/>

</beans>

8.1、bean

 <!-- 指定要扫描的包,这个包下的注解就会生效 -->
<context:component-scan base-package="com.jam"/>
<context:annotation-config/>

8.2、属性如何注入

//等价于 <bean id="user" class="com.jam.pojo.User">
//@Component组件
@Component
public class User {

    //public String name = "Jam";

    // @Value("Jam")
    // 相当于 <property name="name" value="Jam"/>
    @Value("Jam")
    public String name;
}

8.3、衍生的注解

@Component有几个衍生注解,我们在web开发中,会按照mvc三层架构分层。

  • dao【@Repository】
  • service【@Service】
  • controller【@Controller】

    四个注解都是代表将某个类注册到Spring中,装备Bean。

  1. 自动装配

    @Autowired:自动装配通过类型、名字
        如果Autowired不能唯一自动装配上属性,则需要通过@Qualifier(value="xxx")
    @Nullable:字段标记了这个注解,说明这个字段可以为null
    @Resource:自动装配通过类型、名字
  1. 作用域

    package com.jam.pojo;
    
    import org.springframework.beans.factory.annotation.Value;
    import org.springframework.context.annotation.Scope;
    import org.springframework.stereotype.Component;
    
    //等价于 <bean id="user" class="com.jam.pojo.User">
    //@Component组件
    @Component
    @Scope("prototype")
    public class User {
    
        //public String name = "Jam";
    
        // @Value("Jam")
        // 相当于 <property name="name" value="Jam"/>
        @Value("Jam")
        public String name;
    }
    

8.4、小结

xml与注解:

  • xml更加万能,适用于任何场合,维护简单方便。
  • 注解 不是自己的类使用不了,维护相对复杂!

xml与注解最佳实践:

  • xml用来管理bean
  • 注解只负责完成属性的注入
  • 我们在使用的过程中,只需要注意一个问题:必须让注解生效,就需要开启注解的支持

    <!-- 指定要扫描的包,这个包下的注解就会生效 -->
    <context:component-scan base-package="com.jam"/>
    <context:annotation-config/>

9、使用Java的方式配置Spring

项目名:spring-07-appconfig

我们现在要完全不适用Spring的xml配置,全权交给Java来做。

JavaConfig是Spring的一个子项目,在Spring 4之后,它就成为了一个核心功能。

User类

package com.jam.pojo;

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

//这个注解的意思就是说嘛这个类被Spring接管了,注册到了容器中。
@Component
public class User {

    private String name;

    public String getName() {
        return name;
    }

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

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

JamConfig.java 配置文件/配置类

package com.jam.config;

import com.jam.pojo.User;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;

//这个也会被Spring容器接管,注册到容器中。
//@Configuration代表这个是一个配置类,就等于beans.xml
@Configuration
@ComponentScan("com.jam.pojo")
@Import(JamConfig2.class)
public class JamConfig {

    //注册一个bean,就相当于一个bean标签
    //方法的名字相当于bean中的id
    //返回值相当于bean标签中class属性
    @Bean
    public User getUser(){
        return  new User();
    }

}

Test.java 测试类

import com.jam.config.JamConfig;
import com.jam.pojo.User;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class MyTest {

    public static void main(String[] args) {

        //如果完全使用了配置类去做,就只能通过AnnotationConfig上下文来获取容器,通过配置类的class对象加载。
        ApplicationContext context = new AnnotationConfigApplicationContext(JamConfig.class);
        User user = context.getBean("user",User.class);
        System.out.println(user.getName());
    }

}

这种纯Java的配置方式,在SpringBoot中随处可见!

10、代理模式

项目:spring-08-proxy

为什么要学习代理模式?因为这就是SpringAOC的底层!【SpringAOC 和 SpringMVC】

代理模式的分类:

  • 静态代理
  • 动态代理

10.1、静态代理

项目:spring-08-proxy-demo1

角色分析:

  • 抽象角色:一般会使用接口或者抽象类来解决。
  • 真是角色:被代理的角色。
  • 代理角色:代理真实觉得,代理真实角色后,我们一般会做一些附属操作。
  • 客户:访问代理对象的人。

代码步骤:

  1. 接口 Rent.java

    //租房
    public interface Rent {
    
        public void rent();
    
    }
  1. 真实角色 Landlord.java

    //房东
    public class Landlord implements Rent {
    
        public void rent() {
            System.out.println("房东要出租房子");
        }
    }
  1. 代理角色 Proxy.java

    public class Proxy {
    
        private Landlord landlord;
    
        public Proxy() {
        }
    
        public Proxy(Landlord landlord) {
            this.landlord = landlord;
        }
    
        public void  rent(){
            landlord.rent();
            seeHouse();
            fee();
            sign();
        }
    
        //看房
        public void  seeHouse(){
            System.out.println("中介带你看房");
        }
    
        //收中介费
        public void  fee(){
            System.out.println("中介费用");
        }
    
        //租赁合同
        public void  sign(){
            System.out.println("合同");
        }
    }
  1. 客户端访问代理角色 Client.java

    public class Client {
        public static void main(String[] args) {
            //房东
            Landlord landlord = new Landlord();
            //代理 中介帮房东出租房子,代理角色一般会有附属操作
            Proxy proxy = new Proxy(landlord);
            //不用面对房东,直接找中介
            proxy.rent();
        }
    }
  • 可以使用真实角色的操作更加纯粹,不用去关注一些公共的业务。
  • 公共也就交给代理角色,实现了业务的分工。
  • 公共业务发生扩展的时候,方便集中管理。

缺点:

  • 一个真实角色就会产生一个代理角色,代码量翻倍,开发效率变低。

10.2、加深理解

项目:spring-08-proxy-demo1

聊聊AOP

image-20200619152412150

10.3、动态代理

项目:spring-08-proxy-demo3

  • 动态代理和静态代理角色一样。
  • 动态代理的代理类是动态生成的,不是我们直接写好的。
  • 动态代理分为两个类:基于接口的动态代理基于类的动态代理

    • 基于接口的----JDK代理
    • 基于类:cglib
    • java字节码实现:javasist

需要了解两个类

  • Proxy:代理
  • InvocationHandler:调用处理程序

newProxyInstance:

ClassLoader:类加载器
    它是用于加载代理对象字节码的。和被代理对象使用相同的类加载器。固定写法
Class[]:字节码数组
    它是用于让代理对象和被代理对象有相同方法。固定写法。
InvocationHandler:用于提供增强的代码
    它是让我们写我们代理。我们一般都是写一个该接口的现实类,通常情况下都是匿名内部类,但不是不许的。
    此接口的实现类都是谁用谁写。

动态代理的好处:

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

Client.java

package com.jam.demo03;

public class Client {
    public static void main(String[] args) {
        //真实角色
        Landlord landlord = new Landlord();

        //代理角色,现在没有
        ProxyInvocationHandler pih = new ProxyInvocationHandler();
        //通过调用程序处理角色来处理我们要调用的接口
        pih.setRent(landlord);

        Rent proxy = (Rent) pih.getProxy();
        proxy.rent();
    }
}

Landlord.java

//房东
public class Landlord 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);
    }

    //处理代理实例并返回结果
    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("中介费用");
    }
}

Rent.java

//租房
public interface Rent {

    public void rent();

}

10.3.1、cglib动态代理

导包

<dependency>
    <groupId>cglib</groupId>
    <artifactId>cglib</artifactId>
    <version>3.3.0</version>
</dependency>

基于子类的动态代理:Enhancer

如何创建代理对象:使用Enhancer类中的create方法

创建代理对象的要求:被代理类不能是最终类

Class:字节码

​ 它是用于指定被代理对象的字节码

Callback:用于提供增加的代码


下一章:点击访问

本文共 2216 个字数,平均阅读时长 ≈ 6分钟
0

打赏

海报

正在生成.....

评论 (0)

取消