spring笔记

Posted 敲代码的xiaolang

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了spring笔记相关的知识,希望对你有一定的参考价值。

目录

  • 狂神视频地址
    • 1、Spring
      • 1-1.Spring 简介
      • 1-2.Spring 的优点
      • 1-3.Spring 组成
      • 1-4.Spring 拓展
    • 2、IOC理论推导
      • 2-1.UserDao 接口
      • 2-2.UserDaoImpl 实现类
      • 2-3.UserService 业务接口
      • 2-4.UserServiceImpl 业务实现类
      • 2-5.测试
    • 3、IOC本质
    • 4、Spring 入门程序
      • 4-1.实体类
      • 4-2.配置文件
      • 4-3.测试
      • 4-4.结果
      • 4-5.思考问题?
    • 5、IOC创建对象的方式
      • 5-1.使用无参构造创建对象,默认!
      • 5-2.使用有参构造创建对象
      • 5-3.类型
    • 6、Spring配置
      • 6-1.别名
      • 6-2.Bean的配置
      • 6-3.import
    • 7、依赖注入
      • 7-1.构造器注入
      • 7-2.Set 注入
    • 9、bean的作用域
      • 9-1.单例模式(Spring 默认机制)
      • 9-2.原型模式:每次从容器中get的时候,都会产生一个新对象
    • 10.Bean的自动装配
    • 11、注解实现自动装配
      • 11-1.@Autowired
      • 11-2.@Resource
      • 11-3.根据类型和id查找
    • 12、使用注解开发
    • 13、java的方式配置Spring
      • 13-1.主配置类
      • 13-2.配置类2
      • 13-3.User实体类
      • 13-4.测试类
    • 14、代理模式
      • 14-1.角色分析:
      • 14-2.接口
      • 14-3.真实角色
      • 14-4.代理角色
      • 14-5.客户端访问代理角色
      • 14-6.代理模式的好处:
      • 14-7.缺点:
    • 15、静态代理
    • 16、动态代理
    • 17、基于Proxy类和InvocationHandler 实现动态代理
      • 17-1.真实的角色
    • 17-2.被代理的接口
      • 17-3.代理 真实的角色 ProxyInvocationHandler
      • 17-4.用户
      • 17-5.输出结果
    • 18、基于Proxy类和InvocationHandler 实现动态代理 2
    • 19、动态代理和静态代理总结
      • 19-1.静态代理
      • 19-2.动态代理
    • 20、什么是Aop
      • 20-1.Aop在Spring中的作用
    • 21、Aop 实现方式1-基于原生API
      • 21-1.导入依赖
      • 21-2.在执行UserService实现类的所有方法前,增加日志功能
      • 21-3.在执行UserService实现类的所有方法后,增加日志功能
      • 21-4.UserService 接口
      • 21-5.UserServiceImpl 接口
      • 21-6.bean.xml 文件
      • 21-7.测试类
      • 21-7.输出结果
    • 22、Aop 实现方式2-切面方式
      • 22-1.配置文件
      • 22-2.自定义切面
      • 22-3.Usersevice 接口
      • 22-4.UserServiceImpl 实现类
      • 22-5.测试类
      • 22-6.输出
    • 23、Aop 实现方式3-注解方式
      • 23-1.bean配置文件
      • 23-2.自定义类
      • 23-3.UserService 类:
      • 23-4.UserServiceImpl 类
      • 23-5.测试类
      • 23-6.输出结果
    • 24、Spring 整合Mybatis
      • 24-1.导入依赖
      • 24-2.编写数据源配置
      • 24-3.需要给接口加实现类【】
      • 24-4.将自己写的实现类,注入到容器中
      • 24-5.测试
    • 25、声明式事务
      • 25-1.什么是事务
      • 25-2.事务ACID 原则:
      • 25-3.为什么需要事务?
    • 26、声明式事务代码
      • 26-1.spring-dao.xml
      • 26-2.applicationContext.xml
      • 26-3.user 实体类
      • 26-4.UserMaper接口
      • 26-5.UserMapper配置文件
      • 26-6.UserMapper 实现类
      • 26-7.测试

狂神视频地址

https://www.bilibili.com/video/BV1WE411d7Dv?p=3


1、Spring

1-1.Spring 简介

Spring:春天------>给软件行业带来了春天!

2002,首次推出了Spring框架的雏形:interface21框架!

Spring框架即以interface21框架为基础,经过重新设计,并不断丰富其内涵,于2004年3月24日发布了1.0正式版。

Rod Johnson,Spring Framework创始人,著名作者。很难想象Rod Johnson的学历,真的让好多人大吃一惊,他是悉尼大学的博士,然而他的专业不是计算机,而是音乐学

Spring理念:使现有的技术更加容易使用,本身是一个大杂烩,整合了现有的技术框架


1-2.Spring 的优点

  1. Spring是一个开源的免费的框架(容器)!
  2. Spring是一个轻量级的、非入侵式的框架!
  3. 控制反转(IOC),面向切面编程(AOP)!
  4. 支持事务的处理,对框架整合的支持!

总结一句话:Spring就是一个轻量级的控制反转(IOC)和面向切面编程(AOP)的框架!


1-3.Spring 组成


1-4.Spring 拓展

现代化的Java开发!说白就是基于Spring的开发!

Spring Boot

  1. 一个快速开发的脚手架。
  2. 基于SpringBoot可以快速的开发单个微服务。
  3. 约定大于配置。

Spring Cloud

  1. SpringCloud是基于SpringBoot实现的。
  2. 因为现在大多数公司都在使用SpringBoot进行快速开发,学习SpringBoot的前提,需要完全掌握Spring及SpringMVC!承上启下的作用!

弊端:发展了太久之后,违背了原来的理念!配置十分繁琐,人称:“配置地狱!”


2、IOC理论推导

2-1.UserDao 接口

public interface UserDao 

    void getUser();

 

2-2.UserDaoImpl 实现类

public class UserDaoImpl implements UserDao 

    public void getUser() 
        System.out.println("获取用户数据");
    
 

2-3.UserService 业务接口

public interface UserService 

    void getUser();
 

2-4.UserServiceImpl 业务实现类

public class UserServiceImpl implements UserService 

    UserDao userDao = new UserDaoImpl();

    public void getUser() 
      userDao.getUser();
    
 

2-5.测试

public class Test 
    public static void main(String[] args) 

        UserService userService = new UserServiceImpl();
        userService.getUser();

    
 

在我们之前的业务中,用户的需求可能会影响我们原来的代码,我们需要根据用户的需求去修改原代码!如果程序代码量十分大,修改一次的成本代价十分昂贵!

我们使用一个Set接口实现,已经发生了革命性的变化!

UserDao userDao;

public void setUserDao(UserDao userDao)
    this.userDao = userDao;
 

之前,程序是主动创建对象!控制权在程序员手上!

使用了set注入后,程序不再具有主动性,而是变成了被动的接受对象!

控制反转了,主动权交给用户了

这种思想,从本质上解决了问题,我们程序猿不用再去管理对象的创建了。系统的耦合性大大降低~,可以更加专注的在业务的实现上!这是IOC的原型!


3、IOC本质

控制反转IoC(Inversion of Control),是一种设计思想,DI(依赖注入)是实现IoC的一种方法,也有人认为DI只是IoC的另一种说法。没有IoC的程序中,我们使用面向对象编程,对象的创建与对象间的依赖关系完全硬编码在程序中,对象的创建由程序自己控制,控制反转后将对象的创建转移给第三方,个人认为所谓控制反转就是:获得依赖对象的方式反转了

采用XML方式配置Bean的时候,Bean的定义信息是和实现分离的,而采用注解的方式可以把两者合为一体,Bean的定义信息直接以注解的形式定义在实现类中,从而达到了零配置的目的。

控制反转是一种通过描述(XML或注解)并通过第三方去生产或获取特定对象的方式。在Spring中实现控制反转的是IoC容器,其实现方法是依赖注入(Dependency Injection,DI)。


4、Spring 入门程序

4-1.实体类

public class User 
    private String name;

    public String getName() 
        return name;
    

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

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

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

    <!--使用Spring 来创建对象,在Spring 中,这些都称为bean
    类型  变量名 = new 类型();
    User user = new User();

        bean = 对象 new User();
    id = 变量名
    class = new 的对象;
    property 相当于 给对象中的属性设置一个值!

    -->
    <bean id="user" class="cn.bloghut.domain.User">
            <property name="name" value="csdn_闲言"/>
    </bean>

</beans> 

4-3.测试

public static void main(String[] args) 
    //获取Spring 的上下文对象
    ApplicationContext ac = new ClassPathXmlApplicationContext("beans.xml");
    //我们的对象现在都在Spring 中管理了,我们要使用,直接去里面取出来就行了
    User user = (User)ac.getBean("user");
    //打印
    System.out.println(user.toString());
 

4-4.结果

Username='闲言' 

4-5.思考问题?

Hello对象是谁创建的?

Hello对象是由Spring创建的。

Hello对象的属性是怎么设置的?
  Hello对象的属性是由Spring容器设置的。

这个过程就叫控制反转:

控制:谁来控制对象的创建,传统应用程序的对象是由程序本身控制创建的,使用Spring后,对象是由Spring来创建的。

反转:程序本身不创建对象,而变成被动的接收对象。

依赖注入:就是利用set方法来进行注入的。

IOC是一种编程思想,由主动的编程变成被动的接收。

可以通过new ClassPathXmlApplicationContext去浏览一下底层源码。

到了现在,不用在程序中去改动了,要实现不同的操作,只需要在xml配置文件中进行修改,所谓的IOC,一句话搞定:对象由Spring来创建,管理,装配!


5、IOC创建对象的方式

5-1.使用无参构造创建对象,默认!

public User() 
    System.out.println("User 空参构造方法执行了");


<!--注入user-->
<bean id="user" class="cn.bloghut.pojo.User">
    <property name="name" value="csdn_闲言"/>
</bean> 

5-2.使用有参构造创建对象

下标赋值

public User(String name) 
    this.name = name;


<bean id="user" class="cn.bloghut.pojo.User">
    <constructor-arg index="0" value="csdn_闲言"/>
</bean> 

5-3.类型

public User(String name) 
    this.name = name;

<!--通过类型-->
<bean id="user" class="cn.bloghut.pojo.User">
    <constructor-arg type="java.lang.String" value="csdn_闲言"/>
</bean>

参数名
<!--参数名-->
<bean id="user" class="cn.bloghut.pojo.User">
    <constructor-arg name="name" value="csnd_闲言"/>
</bean> 

总结:在配置文件加载的时候,容器中管理的对象就已经初始化了!

Spring 容器,就类似于婚介网站。

  • 信息都注册在里面
  • 你想查看(获取)的时候再拿

6、Spring配置

6-1.别名

<bean id="user" class="cn.bloghut.pojo.User">
    <constructor-arg name="name" value="csnd_闲言"/>
</bean>
<alias name="user" alias="user2"/>

ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
User user = (User)ac.getBean("user2");
user.show();

输出:csnd_闲言 

6-2.Bean的配置

别名可以通过很多种方式分割

空格分割
逗号分割
分号分割
<bean id="user" class="cn.bloghut.pojo.User" name="user2 u2,u3;u4">
    <property name="name" value="csdn_闲言"/>
</bean>

//        User user = (User)ac.getBean("user");
//        User user = (User)ac.getBean("user2");
//        User user = (User)ac.getBean("u2");
//        User user = (User)ac.getBean("u3");
         User user = (User)ac.getBean("u4");
        user.show();

输出:csdn_闲言 

6-3.import

这个import。一般用于团队开发使用,它可以将多个配置文件,导入合并为一个。

假设,现在项目中有多个人开发,这三个人负责不同的类开发,不同的类需要注册在不同的bean中,我们可以利用import将所有人的beans.xml合并为一个总的!

  1. 张三
  2. 李四
  3. 王五

使用的时候,直接使用总的配置就可以了。


7、依赖注入

7-1.构造器注入

7-2.Set 注入

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

Address类

public class Address 
    private String address;
        省略setter
 

student类

public class Student 

    private String name;
    private Address address;
    private String[] books;
    private List<String> hobbies;
    private Map<String,String> card;
    private Set<String> games;
    private String wife;
    private Properties info;
    省略setter
 

bean.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="address" class="cn.bloghut.domain.Address">
        <property name="address" value="csdn_闲言"/>
    </bean>

    <bean id="student" class="cn.bloghut.domain.Student">
        <!--注入address 类型-->
        <property name="address" ref="address"/>
        <!--String 类型-->
        <property name="name" value="csdn_闲言"/>
        <!--String 类型-->
        <property name="books">
            <array>
                <value>JavaSE</value>
                <value>JavaWeb</value>
                <value>Spring</value>
                <value>SpringMVC</value>
                <value>Mybatis</value>
            </array>
        </property>
        <!--List-->
        <property name="hobbies">
            <list>
                <value>唱</value>
                <value>跳</value>
                <value>rap</value>
                <value>篮球</value>
            </list>
        </property>
        <!--Map-->
        <property name="card">
            <map>
                <entry key="闲言" value="csdn——闲言"></entry>
                <entry key="闲言博客" value="csdn——闲言——博客"></entry>
            </map>
        </property>
        <!--set-->
        <property name="games">
            <set>
                <value>唱</value>
                <value>跳</value>
                <value>rap</value>
                <value>篮球</value>
            </set>
        </property>
        <!--String-->
        <property name="wife" value="xxx"></property>
        <!--Properties-->
        <property name="info">
            <props>
                <prop key="p1">v1</prop>
                <prop key="p2">v2</prop>
                <prop key="p3">v3</prop>
            </props>
        </property>
    </bean>

</beans> 

测试

public static void main(String[] args) 
    ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
    Student student = (Student)ac.getBean("student");
    System.out.println(student);
 

输出

Student
name='csdn_闲言', 
address=Addressaddress='csdn_闲言', 
books=[JavaSE, JavaWeb, Spring, SpringMVC, Mybatis], 
hobbies=[唱, 跳, rap, 篮球], 
card=闲言=csdn——闲言, 闲言博客=csdn——闲言——博客, 
games=[唱, 跳, rap, 篮球], 
wife='xxx', 
info=p3=v3, p2=v2, p1=v1
 

## 8、拓展方式注入 可以使用**p命名空间**和**c命名空间**进行注入

  1. p 命名空间对应 setter 方式注入(要提供set方法)
  2. c 命令空间对应 构造方法 (要提供有参构造方法)
<?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">

</beans> 

bean.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">

    <bean id="user" class="cn.bloghut.domain.User" p:name="闲言" p:age="18"/>
    <bean id="user2" class="cn.bloghut.domain.User" c:name="csdn——闲言" c:age="19" />

</beans> 

pojp

public class User 
    private String name;
    private int age;

    public User() 
    

    public User(String name, int age) 
        this.name = name;
        this.age = age;
    
    省略setter
 

测试

ApplicationContext ac = new ClassPathXmlApplicationContext("userbeans.xml");
User user = (User)ac.getBean("user");
User user2 = (User)ac.getBean("user2");
System.out.println(user);
System.out.println(user2); 

输出

Username='闲言', age=18
Username='csdn——闲言', age=19 

9、bean的作用域

9-1.单例模式(Spring 默认机制)

scope="singleton"
<bean  scope="singleton" id="user2" class="cn.bloghut.domain.User" c:name="csdn——闲言" c:age="19" />

User user2 = (User)ac.getBean("user2");
User user3 = (User)ac.getBean("user2");
System.out.println(user2.hashCode());
System.out.println(user3.hashCode()); 

输出

817406040
817406040 

9-2.原型模式:每次从容器中get的时候,都会产生一个新对象

<bean scope="prototype" id="user" class="cn.bloghut.domain.User" p:name="闲言" p:age="18"/>

ApplicationContext ac = new ClassPathXmlApplicationContext("userbeans.xml");
    for (int i = 0;i< 5;i++)
       System.out.println(ac.getBean("user").hashCode());
 

输出

817406040
1955915048
1270855946
2083117811
157683534 

单线程—单例
多线程—多例


10.Bean的自动装配

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

在Spring 中有三种装配的方式

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

ByName方法自动装配

  1. autowire=“byName”
  2. 会自动在容器上下文中查找,和自己对象set方法后面的值对应的bean id!
  3. 弊端:set 方法后面的值和 id 相同

bean.xml

<bean id="cat" class="cn.bloghut.domin.Cat"/>
<bean id="dog" class="cn.bloghut.domin.Dog"/>

<!--
    byName:会自动在容器上下文中查找,和自己对象set方法后面的值对应的bean id!

-->
<bean id="people" class="cn.bloghut.domin.People" autowire="byName">
    <property name="name" value="csdn_闲言"/>
</bean> 

测试

public static void main(String[] args) 

    ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
    People people = ac.getBean("people", People.class);

    people.getCat().shout();
    people.getDog().shout();

 

ByName方法自动装配

  1. autowire=“byType”
  2. 会自动在容器上下文中查找,和自己对象属性类型相同的bean
  3. 弊端:它必须保证类型全局唯一(在IOC容器中只能由一个)。
<bean id="cat" class="cn.bloghut.domin.Cat"/>
<bean id="dog11" class="cn.bloghut.domin.Dog"/>
   <!--
    byType:会自动在容器上下文中查找,和自己对象属性类型相同的bean

-->
  <bean id="people" class="cn.bloghut.domin.People" autowire="byType">
      <property name="name" value="csdn_闲言"/>
  </bean> 

测试

public static void main(String[] args) 

    ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
    People people = ac.getBean("people", People.class);

    people.getCat().shout();
    people.getDog().shout();

 

总结:

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


11、注解实现自动装配

  1. jdk1.5支持的注解 Spring2.5支持的注解

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

  3. @Autowired

  4. @Quelifier

  5. @Resource

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

   <!--开启注解支持-->
   <context:annotation-config/>
</beans> 

11-1.@Autowired

  1. 在属性上使用
  2. 在set方式上使用
  3. 使用Autowired 可以不用编写set方法了,前提是你这个自动装配的属性在IOC(Spring)容器中存在(需要通过其他方式注入进容器),且符合名字byName。
public class People 
    @Autowired
    private Dog dog;
    
    @Autowired
    private Cat cat;
    private String name;

    public People() 
    

    public People(Dog dog, Cat cat, String name) 
        this.dog = dog;
        this.cat = cat;
        this.name = name;
    
    省略setter
 

Autowired 有一个个唯一的属性( required )

@Target(ElementType.CONSTRUCTOR, ElementType.METHOD, ElementType.PARAMETER, ElementType.FIELD, ElementType.ANNOTATION_TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Autowired 
    boolean required() default true;
 

@Nullable
  字段标记了这个注解,表示这个字段可以为null

@Qualifier

  • 当我们的容器存在多个相同类型,不同名称的bean。使用@Autowired 无法完成自动装配了
  • 这个时候需要使用@Qualifier 和@Autowired 注解一起使用。
  • 使用@Qualifier 指定一个唯一的bean对象注入!
    例如

IOC容器中存在多个 相同类型不同id的bean

通过@Qualifier 指定一个唯一的bean

测试


11-2.@Resource

不指定name值,先去判断byNamebyType,有一个能注入即成功

根据类型查找

11-3.根据类型和id查找

如果IOC容器中存在多个不同名称,相同类型的bean,可以通过@Resource注解的name属性指定唯一的id;


总结:

  1. 都是用来自动装配的,都可以放在*属性字段*上
  2. @Autowired通过byType(类型)的方式实现,而且必须要求这个对象存在
  3. @Resource默认通过byName(id)的方式实现,如果找不到名字,则通过byType实现!如果两个都找不到的情况下,就报错!
  4. 执行顺序不同:@Autowired通过byType的方式实现。@Resource默认通过byName的方式实现。

12、使用注解开发

在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"
       xmlns:aop="http://www.springframework.org/schema/aop"
       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
        http://www.springframework.org/schema/aop
        https://www.springframework.org/schema/aop/spring-aop.xsd">
    <!--开启注解扫描-->
    <context:component-scan base-package="cn.bloghut"/>

    <!--开启注解支持-->
    <context:annotation-config/>

</beans> 

bean注入使用**@Componet**注解

等价<bean id="user" class="cn.bloghut.domain.User">

@Component
public class User 
   
 

属性注入使用@Value注解

<property name="name" value="闲言">
@Component
public class User 

    private String name;

    @Value("闲言")
    public void  setName(String name)
        this.name = name;
    
 

衍生注解

  • @Componet有几个衍生注解,在web开发中,会按照mvc三层架构分层!
  • @Service--------业务层注解
  • @Repository—持久层注解
  • @Controller-----控制层注解

这四个注解功能都是一样的,都是代表将某个类注册到Spring中,装配Bean

自动装配

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

作用域

@Scope(“singleton”)单例 

总结

XML 与 注解

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

XML 与 注解最佳实践

  • xml用来管理bean
  • 注解只负责完成属性的注入
  • 我们在使用过程中,只需要注意一个问题:必须让注解生效,就需要开启注解的支持
<!--开启注解扫描-->
<context:component-scan base-package="cn.bloghut"/>

<!--开启注解支持-->
<context:annotation-config/> 

13、java的方式配置Spring

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

  1. 首先定义一个类,在类上添加@Component注解,让它加载到Spring IOC容器(让Spring 管理)
  2. 定义Java 配置类,在其类上添加@Configuration 注解,说明该类是一个配置类,这个类也会被Spring 托管,因为它本身是一个@Component
  3. 在Myconfig 类中 添加getUser 方法,返回一个user对象
  4. getUser 方法上的@Bean 注解 则是注册一个bean功能,相当于
<bean id="getUser" class="cn.bloghut.domain.User"> 
  1. 方法的名字,相当于bean 标签中的id 属性。
  2. 方法的返回值,相当于bean 标签中的class属性。(因为我们导包了,Spring 知道是哪一个)
  3. 如果完全使用了配置类方式去做,我们只能通过AnnotationConfigApplicationContext 上下文来获取容器,通过配置类的class 对象加载!

完整代码

  • @Configuration 这个一个配置类
  • @ComponentScan 用于扫描包
  • @Import 用于导入其他配置类

13-1.主配置类

@Configuration
@ComponentScan(basePackages = "cn.bloghut.domain")
@Import(MyConfig2.class)
public class MyConfig 

    /**
     * 返回一个User Bean
     * @return
     */
    @Bean
    public User getUser()
        return new User();
    

 

13-2.配置类2

@Configuration
public class MyConfig2 

 

13-3.User实体类

@Component
public class User 
    private String name;

    public String getName() 
        return name;
    
    @Value("闲言")
    public void setName(String name) 
        this.name = name;
    

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

13-4.测试类

public class MyTest 
    public static void main(String[] args) 

        ApplicationContext ac = new AnnotationConfigApplicationContext(MyConfig.class);
        User user = ac.getBean("getUser", User.class);
        System.out.println(user);

    
 

14、代理模式

14-1.角色分析:

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

代码步骤:

14-2.接口

public interface Rent 
    /**
     * 租房
     */
    void rent();
 

14-3.真实角色

public class Host implements Rent

    @Override
    public void rent() 
        System.out.println("房东要出租房子");
    

 

14-4.代理角色

public class Proxy implements Rent

    private Host host;

    public Proxy() 
    

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

    @Override
    public void rent() 
        seeHouse();
        host.rent();
        fare();
        hetong();
    
    //看房
    public void seeHouse()
        System.out.println("中介带你看房");
    

    //收中介费
    public void fare()
        System.out.println("收中介费");
    
    //签合同
    public void hetong()
        System.out.println("签合同");
    
 

14-5.客户端访问代理角色

public class Client 
    public static void main(String[] args) 

        Host host = new Host();
        //代理,中介帮房东,但是呢?代理角色一般会有一些附属操作!
        Proxy proxy = new Proxy(host);
        //你不用面对房东,直接找中介租房即可!
        proxy.rent();
    
 

14-6.代理模式的好处:

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

14-7.缺点:

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



15、静态代理

  1. 有一天,公司领导要求我为 某个类的所有方法新增日志输出功能。
  2. 怎么做呢?
  3. 在原来的类上的每一个方法添加日志输出?
  4. 这就是改变原来人家的代码了。
  5. 改动原有的业务代码,在公司中是大忌!
  6. 有可能人家你修改了人家的代码,可能给改蹦了。
  7. 新增一个类,制作成本小,职责单一。

原来的开发方式(纵向开发)

添加日志功能(横切进去)

代码如下:
业务接口:

public interface UserService 
    void add();

    void delete();

    void update();

    void query();
 

原来业务类

public class UserServiceImpl 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 query() 
        System.out.println("查询用户");
    
 

代理类

public class UserServiceProxy implements UserService 

    private UserService userService;

    public void setUserService(UserService userService) 
        this.userService = userService;
    

    @Override
    public void add() 
        log("add");
        userService.add();
    

    @Override
    public void delete() 
        log("delete");
        userService.delete();
    

    @Override
    public void update() 
        log("update");
        userService.update();
    

    @Override
    public void query() 
        System.out.println("query");
        userService.query();
    

    //日志方法
    public void log(String msg)
        System.out.println("【Debug 】使用了"+msg+"方法");
    
 

测试类

public static void main(String[] args) 

    UserServiceProxy userService = new UserServiceProxy();
    userService.setUserService(new UserServiceImpl());
    userService.add();
 

16、动态代理

  • 动态代理和静态代理角色一样
  • 动态代理的代理类是动态生成的,不是我们直接写好的
    动态代理分为两大类:基于接口的动态代理,基于类的动态代理
  • 基于接口:JDK动态代理
  • 基于类: cglib
  • java字节码实现: javasist

需要了解两个类:Proxy :代理 InvocationHandler:调用处理程序

java.lang.reflect.Proxy 

Proxy提供了创建动态代理类和实例的静态方法,它也是由这些方法创建的所有动态代理类的超类。(大白话:这是一个静态类,类里边有方法得到代理类)

动态代理类 (以下简称为代理类 )是一个实现在类创建时在运行时指定的接口列表的类,具有如下所述的行为。 代理接口是由代理类实现的接口。 代理实例是代理类的一个实例。 每个代理实例都有一个关联的调用处理程序对象,它实现了接口

通过其代理接口之一的代理实例上的方法调用将被分派到实例调用处理程序的invoke方法,传递代理实例, java.lang.reflect.Method被调用方法的java.lang.reflect.Method对象以及包含参数的类型Object Object的数组。 调用处理程序适当地处理编码方法调用,并且返回的结果将作为方法在代理实例上调用的结果返回。

代理类具有以下属性:

  1. 代理类是公共的,最终的,而不是抽象的,如果所有代理接口都是公共的。
  2. 如果任何代理接口是非公开的,代理类是非公开的,最终的,而不是抽象的 。
  3. 代理类的不合格名称未指定。 然而,以字符串"$Proxy"开头的类名空间应该保留给代理类。
  4. 一个代理类扩展了java.lang.reflect.Proxy 。
  5. 代理类完全按照相同的顺序实现其创建时指定的接口。
  6. 如果一个代理类实现一个非公共接口,那么它将被定义在与该接口相同的包中。 否则,代理类的包也是未指定的。 请注意,程序包密封不会阻止在运行时在特定程序包中成功定义代理类,并且类也不会由同一类加载器定义,并且与特定签名者具有相同的包。
  7. 由于代理类实现了在其创建时指定的所有接口, getInterfaces在其类对象上调用getInterfaces将返回一个包含相同列表接口的数组(按其创建时指定的顺序),在其类对象上调用getMethods将返回一个数组的方法对象,其中包括这些接口中的所有方法,并调用getMethod将在代理接口中找到可以预期的方法。
  8. Proxy.isProxyClass方法将返回true,如果它通过代理类 - 由Proxy.getProxyClass返回的类或由Proxy.newProxyInstance返回的对象的类 - 否则为false。
  9. 所述java.security.ProtectionDomain代理类的是相同由引导类装载程序装载系统类,如java.lang.Object ,因为是由受信任的系统代码生成代理类的代码。 此保护域通常将被授予java.security.AllPermission 。
  10. 每个代理类有一个公共构造一个参数,该接口的实现InvocationHandler ,设置调用处理程序的代理实例。 而不必使用反射API来访问公共构造函数,也可以通过调用Proxy.newProxyInstance方法来创建代理实例,该方法将调用Proxy.getProxyClass的操作与调用处理程序一起调用构造函数。

17、基于Proxy类和InvocationHandler 实现动态代理

17-1.真实的角色

public class Host implements Rent

    public void rent() 
        System.out.println("租房");
    
 

17-2.被代理的接口

public interface Rent 

    void rent();

 

17-3.代理 真实的角色 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);
    
    /**
     * 处理代理实例,并返回结果
     * @param proxy
     * @param method
     * @param args
     * @return
     * @throws Throwable
     */
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable 
        //动态代理的本质,就是使用反射机制
        //在方法调用前调用
        seeHouse();
        Object result = method.invoke(rent,args);
        //在方法调用后调用
        heTong();
        return result;
    
    public void seeHouse()
        System.out.println("看房子");
    
    public void heTong()
        System.out.println("签合同");
     

17-4.用户

public class Client 
    public static void main(String[] args) 
        //创建真实角色
        Host host = new Host();
        //创建代理角色 不存在
        ProxyInvocationHandler proxyInvocationHandler = new ProxyInvocationHandler();
        //设置要代理的对象
        proxyInvocationHandler.setRent(host);
        //获取代理对象,并强制转换
        Rent proxy = (Rent)proxyInvocationHandler.getProxy();
        //调用
        proxy.rent();
    
 

17-5.输出结果

看房子
租房
签合同 

18、基于Proxy类和InvocationHandler 实现动态代理 2

Proxy:生成动态代理实例的
InvocationHandler:调用处理程序并返回结果的

万能的自动生成代理类

package cn.bloghut.demo3;

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

/**
 * @Classname ProxyInvocationHandler
 * @Description 自动生成代理类
 * @Date 2021/5/17 15:44
 * @Created by 闲言
 */
public class ProxyInvocationHandler implements InvocationHandler 

    //1.被代理的接口
    public Object target;

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

    //2.生成得到代理类(代理谁)
    public Object getProxy()
        return Proxy.newProxyInstance(this.getClass().getClassLoader(),
                target.getClass().getInterfaces(),this);
    

    //3.处理代理实例,并返回结果(代用代理程序)
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable 
        //动态获取方法名称
        log(method.getName());
        Object result = method.invoke(target, args);
        return result;
    
    /**
     * 打印日志方法
     * @param msg
     */
    public void log(String msg)
        System.out.println("[debug]===> 执行可"+msg+"方法");
    
 
public static void main(String[] args) 

    //真实角色
    UserServiceImpl userService = new UserServiceImpl();
    //代理角色,不存在
    ProxyInvocationHandler invocationHandler = new ProxyInvocationHandler();
    //设置要代理的对象
    invocationHandler.setTarget(userService);
    //获取代理对象
    UserService proxy = (UserService)invocationHandler.getProxy();
    //执行方法
    proxy.query();
 
[debug]===> 执行可query方法
query 

19、动态代理和静态代理总结

19-1.静态代理

  1. 由程序员创建或由特定工具自动生成源代码,再对其编译。在程序运行前,代理类的.class文件就已经存在了
  2. 静态代理通常

    以上是关于spring笔记的主要内容,如果未能解决你的问题,请参考以下文章

    我们一起学习Spring之Spring简介

    快速构建Spring Cloud工程

    快速构建Spring Cloud工程

    Spring Cloud学习笔记-007

    Spring Initializr 构建Spring Boot/Cloud工程

    Spring MVC官方文档学习笔记之DispatcherServlet