栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

狂神Spring - 个人整理

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

狂神Spring - 个人整理

1、Spring 1.1 简介

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

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

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

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

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

SSH:Struct2 + Spring + Hibernate!

SSM:SpringMVC + Spring + Mybatis!

官网:https://spring.io/projects/spring-framework#overview
官方下载地址:https://repo.spring.io/release/org/springframework/spring/

GitHub:https://github.com/spring-projects/spring-framework


    org.springframework
    spring-webmvc
    5.2.0.RELEASE



    org.springframework
    spring-jdbc
    5.2.0.RELEASE

1.2 优点

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

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

1.3 组成

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-SZ9MmbiN-1646488562911)(D:学习网课学习4_Spring狂神笔记图片1.png)]

1.4 拓展

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

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6bV9RIMI-1646488562912)(D:学习网课学习4_Spring狂神笔记图片10.png)]

Spring Boot

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

SpringCloud是基于SpringBoot实现的。

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

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

2、IOC理论推导

    UserDao 接口

    public interface UserDao {
        void getUser();
    }
    

    UserDaoImpl 实现类

    public class UserDaoImpl implements UserDao {
        public void getUser() {
            System.out.println("默认获取用户数据");
        }
    }
    
    public class UserDaoMySQLImpl implements UserDao {
        @Override
        public void getUser() {
            System.out.println("MySQL获取数据");
        }
    }
    
    public class UserDaoOracleImpl implements UserDao {
        @Override
        public void getUser() {
            System.out.println("Oracle获取用户数据");
        }
    }
    

    UserService 业务接口

    public interface UserService {
        void getUser();
    }
    

    UserServiceImpl 业务实现类

    public class UserServiceImpl implements UserService {
        //创建哪一种UserDao的实现类,在代码中写死了,如果需要变更则需要改变源代码
        private UserDao userDao = new UserDaoImpl();
    
        public void getUser() {
            userDao.getUser();
        }
    }
    

    测试

    public class MyTest {
        public static void main(String[] args) {
    
            //用户实际调用的是业务层,dao层他们不需要接触!
            UserService userService = new UserServiceImpl();
            userService.getUser();
        }
    }
    

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

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BQNVxFVV-1646488562913)(D:学习网课学习4_Spring狂神笔记图片2.png)]

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

public class UserServiceImpl implements UserService {
 		private UserDao userDao;
    //利用set进行动态实现值的注入
    public void setUserDao(UserDao userDao){
        this.userDao = userDao;
    }
    public void getUser() {
        userDao.getUser();
    }
}

之前,程序是主动创建对象!控制权在程序猿手上!(每一次用户的需求都要去改变源代码)使用了set注入后,程序不再具有主动性,而是变成了被动的接收对象!外部只要传入 一个任意的UserDao的实现类,就可以创建出对应的对象。

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

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Lyq2fqV8-1646488562914)(D:学习网课学习4_Spring狂神笔记图片3.png)]

IOC本质

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

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

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

3、HelloSpring

    新建一个maven项目,编写实体类

    public class Hello {
        private String str;
    
        public String getStr() {
            return str;
        }
    
        public void setStr(String str) {
            this.str = str;
        }
    
        @Override
        public String toString() {
            return "Hello{" +
                    "str='" + str + ''' +
                    '}';
        }
    }
    

    编写xml配置文件

    
    
    
    
        
        
            
        
    
    

    测试

    public class MyTest {
        public static void main(String[] args) {
            //获取Spring的上下文对象!
            ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
    
            //我们的对象现在都在Spring中的管理了,我们需要使用,直接去里面取出来就可以!
            Hello hello = (Hello) context.getBean("hello");
            System.out.println(hello.toString());
        }
    }
    

思考问题?

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

这个过程就叫控制反转:

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

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

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

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

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

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

4、IOC创建对象的方式

    使用无参构造创建对象,默认!

    假设我们要使用有参构造创建对象。

      下标赋值

      
          
      
      

      类型

      
          
      
      

      参数名

      
          
      
      

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

5、Spring配置 5.1 别名

5.2 Bean的配置

    

5.3 import

这个import。一般用于团队开发使用,它可以将多个配置文件,导入合并为一个。
假设,现在项目中有多个人开发,这三个人负责不同的类开发,不同的类需要注册在不同的bean中,我们可以利用import将所有人的beans.xml合并为一个总的!

张三

李四

王五

applicationContext.xml




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

注意:如果存在id相同的bean时,相同id相同内容合并,相同id不同内容后来居上覆盖

6、依赖注入(DI) 6.1 构造器注入

前面已经介绍过,参考4、IOC创建对象的方式

6.2 Set方式注入【重点】

依赖注入:Set注入

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

【环境搭建】

    复杂类型

    public class Address {
        private String address;
    
        public String getAddress() {
            return address;
        }
    
        public void setAddress(String address) {
            this.address = address;
        }
    }
    

    真实测试对象

    public class Student {
    
        private String name;
        private Address address;
        private String[] books;
        private List hobbies;
        private Map card;
        private Set games;
        private String wife;
        private Properties info;
    }
    

    beans.xml

    
    
    
        
            
            
        
    
    

    测试类

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

    完善注入信息

    
    
    
        
            
        
    
        
            
            
    
            
            
    
            
            
                
                    红楼梦
                    西游记
                    水浒传
                    三国演义
                
            
    
            
            
                
                    听歌
                    敲代码
                    看电影
                
            
    
            
            
                
                    
                    
                
            
    
            
            
                
                    LOL
                    COC
                    BOB
                
            
    
            
            
                
            
    
            
            
                
                    20190525
                    
                    root
                    root
                
            
    
        
    
    
    

    运行结果

    Student{
    	name='秦疆', 
    	address=Address{address='西安'}, 
    	books=[红楼梦, 西游记, 水浒传, 三国演义], 
    	hobbys=[听歌, 敲代码, 看电影], 
    	card={身份证=11111122222223333, 银行卡=1312312312312312123},
        games=[LOL, COC, BOB],
        wife='null', 
        info={
        	password=root, 
        	url=男, 
        	driver=20190525, 
        	username=root}
        }
    }
    
6.3 拓展方式注入(p,c,util)

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

    官方解释:

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-FJ2kPyCu-1646488562915)(D:学习网课学习4_Spring狂神笔记图片4.png)]

    使用:

    
    
    
        
        
    
        
        
    
    
    

    测试:

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

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

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

    util命名空间 把集合注入部分提取出来

    使用:

    
    
        
         
    	
    		易筋经 
        	九阴真经 
        	九阳神功 
    	 
    
         
    	 
        	 
    	 
    
    
    
6.4 bean的作用域

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-eZ4iAq9T-1646488562915)(D:学习网课学习4_Spring狂神笔记图片16.png)]

    单例模式singleton(Spring默认机制)

    
    

    原型模式prototype:每次从容器中getBean的时候,都会产生一个新对象!

    
    

    其余的request、session、application、这些只能在web开发中使用到!

7、Bean 生命周期 7.1 生命周期

从对象创建到对象销毁的过程

7.2 bean 生命周期

(1) 通过构造器创建 bean 实例(无参数构造)

(2) 为 bean 的属性设置值和对其他 bean 引用(调用 set 方法)

(3) 调用 bean 的初始化的方法(需要进行配置初始化的方法)

(4) bean 可以使用了(对象获取到了)

(5) 当容器关闭时候,调用 bean 的销毁的方法(需要进行配置销毁的方法)

7.3 演示 bean 生命周期

1.Dao类:

public class Orders {
    //无参数构造 
    public Orders() { 
        System.out.println("第一步 执行无参数构造创建 bean 实例"); 
    } 
    private String oname; 
    public void setOname(String oname) { 
        this.oname = oname; 
        System.out.println("第二步 调用 set 方法设置属性值"); 
    } 
    //创建执行的初始化的方法 
    public void initMethod() { 
        System.out.println("第三步 执行初始化的方法"); 
    } 
    //创建执行的销毁的方法 
    public void destroyMethod() { 
        System.out.println("第五步 执行销毁的方法"); 
    } 
} 

2.beans.xml:


    

3.测试

@Test 
public void testBean3() { 
    // ApplicationContext context = 
    // new ClassPathXmlApplicationContext("bean4.xml"); 
    ClassPathXmlApplicationContext context = 
        new ClassPathXmlApplicationContext("bean4.xml"); 
    Orders orders = context.getBean("orders", Orders.class); 
    System.out.println("第四步 获取创建 bean 实例对象"); 
    System.out.println(orders); 
    //手动让 bean 实例销毁 
    context.close(); 
}

4.运行结果

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ysBVyRgN-1646488562916)(D:学习网课学习4_Spring狂神笔记图片17.png)]

7.4 bean的后置处理器

1. bean生命周期有七步,分别是

(1) 通过构造器创建 bean 实例(无参数构造)

(2) 为 bean 的属性设置值和对其他 bean 引用(调用 set 方法)

(3) 把 bean 实例传递 bean 后置处理器的方法 postProcessBeforeInitialization

(4) 调用 bean 的初始化的方法(需要进行配置初始化的方法)

(5) 把 bean 实例传递 bean 后置处理器的方法 postProcessAfterInitialization

(6) bean 可以使用了(对象获取到了)

(7) 当容器关闭时候,调用 bean 的销毁的方法(需要进行配置销毁的方法)

2. 演示添加后置处理器效果

    创建类,实现接口 BeanPostProcessor,创建后置处理器

    public class MyBeanPost implements BeanPostProcessor { 
        @Override 
        public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { 
            System.out.println("在初始化之前执行的方法"); 
            return bean; 
        } 
        @Override 
        public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { 
            System.out.println("在初始化之后执行的方法"); 
            return bean; 
        } 
    }
    

    在xml配置 后置处理器

    
    

    测试

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-RYgKPAA5-1646488562916)(D:学习网课学习4_Spring狂神笔记图片18.png)]

8、Xml的自动装配

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

在Spring中有三种装配的方式:

    在xml中显式的配置;在java中显式配置;隐式的自动装配bean【重要】
8.1 测试

环境搭建:创建项目,一个人有两个宠物!





    
    
    

8.2 ByName自动装配

    

8.3 ByType自动装配

    

小结:

ByName的时候,需要保证所有bean的id唯一,并且这个bean需要和自动注入的属性的set方法的值一致!ByType的时候,需要保证所有bean的class唯一,并且这个bean需要和自动注入的属性的类型一致!它可以省略这个bean的属性id。 8.4 使用注解实现自动装配

jdk1.5支持的注解,Spring2.5就支持注解了!

要使用注解须知:

    导入约束 : context约束

    配置注解的支持 : context:annotation-config/

    
    
    		
    		
            
    
    
@Autowired

默认按照类型(ByType)自动装配,如果存在多个同类型实例,再和@Qualifier搭配来使用按照名称(byName)装配。

直接在属性上使用即可!也可以在set方法上使用!

使用Autowired我们就可以不用编写set方法了,前提是你这个自动配置的属性在IOC(Spring)容器中存在,且符合类型ByType!

即默认情况下,它具有强制契约特性,其所标注的属性必须是可装配的。如果没有Bean可以装配到Autowired所标注的属性或参数中,那么你会看到NoSuchBeanDefinitionException的异常信息。当然,如果我们不确定属性是否可以装配,如果要允许 null 值,可以这样来使用Autowired。

//其中,false,对象可以为null;默认是true,对象必须存对象,不能为null。
@Autowired(required = false)
private Cat cat;

它不用显式的在bean中写出依赖的对象,它会自动的匹配其它bean中id名与本bean的set**相同的,并自动装载。

注意点:我记得曾经有个面试题是这样问的:Autowired是按照什么策略来自动装配的呢?

关于这个问题,不能一概而论,你不能简单的说按照类型或者按照名称。但可以确定的一点的是,它默认是按照类型来自动装配的,即byType;如果匹配到同个类型的多个实例,再通过byName来确定Bean,也就是此时需要通过利用@Qualifier指定id和@Autowired搭配来使用ByName的方式。

科普:@Nullable

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

科普:@Autowired (required = false)

测试代码

public class People {
    //如果显式定义了Autowired的required属性为false,说明这个对象可以为null,并且对象为null时会报空指针异常,否则不允许为空
    @Autowired(required = false)
    private Cat cat;
    @Autowired
    private Dog dog;
    private String name;
}
@Qualifier

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

public class People {
    @Autowired
    @Qualifier(value = "cat111")
    private Cat cat;
    @Autowired
    @Qualifier(value = "dog222")
    private Dog dog;
    private String name;
}
@Resource

它是java的注解,时@Autowired和@Qualifier的集合体

默认按名称装配,当找不到与名称匹配的 bean 时才按照类型进行装配。名称可以通过 name 属性指定,如果没有指定 name 属性,当注解写在字段上时,默认取字段名,当注解写在 setter 方法上时,默认取属性名进行装配。

注意:如果 name 属性一旦指定,就只会按照名称进行装配。

import javax.annotation.Resource;
public class People {

    //如果匹配到同个类型的多个实例,可以用 @Resource(name = "xxx")
    @Resource(name = "cat2")
    private Cat cat;

    @Resource
    private Dog dog;
}

小结:

@Resource和@Autowired的区别:

都是用来自动装配的,都可以放在属性字段上@Autowired通过byType的方式实现,而且必须要求这个对象存在!如果匹配到同个类型的多个实例,使用ByName进行装配【常用】@Resource默认通过byName的方式实现(即默认取字段名进行按照名称查找),如果找不到名字,则通过byType实现!如果两个都找不到的情况下,就报错!【常用】执行顺序不同:@Autowired是先通过byType的方式实现。@Resource是先通过ByName的方式实现的。@Resource如果两种方式都找不到的情况下(即匹配到同个类型的多个实例),则需要通过@Resource(name=“xxx”)来指定名称注入 9、使用注解开发

在Spring4之后,要使用注解开发,必须要保证aop的包导入了

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-yvKXJUOw-1646488562917)(D:学习网课学习4_Spring狂神笔记图片6.png)]

使用注解需要导入context约束,增加注解的支持!




    
    
    
    

拓展:注解扫描配置细节

 
 
     
 

 
 
     

    bean

    属性如何注入

    //等价于
    //@Component 组件
    
    @Component
    public class User {
    
        //相当于  
        //它也可以放在set方法上面实现相同效果
        @Value("白莲")
        public String name;
    }
    

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

    dao 【@Repository】
    service 【@Service】
    controller 【@Controller】
    无法按照下面三个注解分类,就用此注解 【@Component】
    

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

    自动装配

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

    作用域

    @Component
    @Scope("singleton")
    public class User {
    
        //相当于  
        @Value("白莲")
        public String name;
    }
    

    小结

    xml与注解:

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

    xml与注解最佳实践:

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

    
    
10、使用Java的方式配置Spring(完全注解开发)

我们现在要完全不使用Spring的xml配置了,全权交给Java来做!
JavaConfig是Spring的一个子项目,在Spring4之后,它成为了一个核心功能!

10.1 创建配置类,替代xml 配置文件

    创建配置类,替代xml 配置文件

    //@Configuration:作为配置类,替代 xml 配置文件,此时不需要开启注解扫描也可以使用@Bean来注册一个Bean
    //@ComponentScan:注解定义要扫描的路径从中找出标识了需要装配的类自动装配到 Spring 的 bean 容器中
    //@import:将多个配置类融为一个配置类
    @Configuration  //这个也会被Spring容器托管,注册到容器中,因为它本来就是一个 @Component
    @ComponentScan(basePackages = {"com.cyan"})
    @import(SpringConfig2.class)
    public class SpringConfig { 
    	
        //里面可以通过@Bean注解来注册一个Bean
        //比如当我们引用第三方库中的类需要装配到 Spring 容器时,只能通过 @Bean 来实现。
    } 
    

    编写测试类

    @Test 
    public void testService2() { 
        //加载配置类 
        //如果完全使用了配置类方式去做,我们就只能通过 AnnotationConfig 上下文来获取容器,通过配置类的class对象加载!
        ApplicationContext context 
                = new AnnotationConfigApplicationContext(SpringConfig.class); 
        User user = context.getBean("user", User.class); 
        System.out.println(user.getName()); 
    } 
    
10.2 拓展:关于@Bean和@Component区别:
1.注解作用
- @Component注解 表明一个类会作为组件类,并告知Spring要为这个类创建bean。
- @Bean注解 告诉Spring这个方法将会返回一个对象,这个对象要注册为Spring应用上下文中的bean。通常方法体中包含了最终产生bean实例的逻辑。

2.两者对比
- 相同点:两者的结果都是为spring容器注册Bean.
- 不同点:@Component 通常是通过类路径扫描来自动侦测以及自动装配到Spring容器中。
		@Bean 注解通常是我们在标有该注解的方法中定义产生这个bean的逻辑。
		
3.理解    
- @Component (@Controller @Service @Respository)作用于类上,只有在我们的SpringBoot应用程序启用了组件扫描并且包含了被注解的类时才有效。通过组件扫描,Spring将扫描整个类路径,并将所有@Component注释类添加到Spring Context,这里有的不足就是会把整个类当成bean注册到spring 容器上,如果这个类中并不是所有方法都需要注册为bean的话,会出现不需要的方法都注册成为bean,这时候必须确保这些不需要的方法也能注册为bean或者在扫描中加filter 过滤这些不需要的bean,否者spring将无法成功启动。

- @Bean相对来说就更加灵活了,它可以独立加在方法上,按需注册到spring容器,而且如果你要用到第三方类库里面某个方法的时候,你就只能用@Bean把这个方法注册到spring容器,因为用@Component你需要配置组件扫描到这个第三方类路径而且还要在别人源代码加上这个注解,很明显是不现实的。
10.2.1 @Bean使用示例:

配置类:

@Configuration
public class SpringConfig {
    // @Bean注解作用于方法
    // 注册一个bean,就相当于我们之前写的一个bean标签
    // 这个方法的名字,就相当于bean标签中id属性
    // 这个方法的返回值,就相当于bean标签中的class属性
    @Bean
    public User getUser(){
        return new User();  // 就是返回要注入到bean的对象!
    }
}

实体类:

public class User {

    private String name;

    public String getName() {
        return name;
    }

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

}

测试类:

public class MyTest {
    @Test
    public void test1(){
        //如果完全使用了配置类方式去做,我们就只能通过 AnnotationConfig 上下文来获取容器,通过配置类的class对象加载!
        ApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig.class);
        User getUser = (User)context.getBean("getUser");    //此时只需要@Bean注册过的方法名来作为一个bean
        System.out.println(getUser.getName());
    }
}
10.2.2 @Component使用示例

配置文件:

// 这个也会Spring容器托管,注册到容器中,因为它本来就是一个@Component
// @Configuration代表这是一个配置类,就和我们之前看的beans.xml
@Configuration
@ComponentScan("com.kuang.pojo")
public class KuangConfig {

}

实体类:

//这里这个注解的意思,就是说明这个类被Spring接管了,是通过路径扫描来自动侦测以及自动装配到 Spring 容器中
//我们可以使用 @ComponentScan 注解定义要扫描的路径从中找出标识了需要装配的类自动装配到 Spring 的 bean 容器中
@Component
public class User {
    private String name;

    public String getName() {
        return name;
    }

    @Value("黑心白莲") //属性注入值
    public void setName(String name) {
        this.name = name;
    }

}

测试类:

public class MyTest {
    @Test
    public void test1(){
        //如果完全使用了配置类方式去做,我们就只能通过 AnnotationConfig 上下文来获取容器,通过配置类的class对象加载!
        ApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig.class);
        User user = (User)context.getBean("user");    //此时只需要@Bean注册过的方法名来作为一个bean
        System.out.println(user.getName());
    }
}

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

11、代理模式

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

代理模式的分类:

静态代理动态代理

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-PDDylwJr-1646488562917)(D:学习网课学习4_Spring狂神笔记图片8.png)]

11.1 静态代理

角色分析:

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

代码步骤:

    接口

    //租房  接口
    public interface Rent {
        //出租房屋方法
        public void rent();
    }
    

    真实角色

    //房东   真实角色(被代理对象)
    public class Host implements Rent {
    
        public void rent() {
            System.out.println("房东要出租房子!");
        }
    }
    
    

    代理角色

    //中介  :   代理角色(代理对象)
    public class Proxy implements Rent {
    
        private Host host;
    
        public Proxy(){
        }
    
        public Proxy(Host host){
            this.host = host;
        }
    
        public void rent() {
            seeHouse();
            //帮房东租出房子
            host.rent();
            hetong();
            fare();
        }
    
        //看房
        public void seeHouse() {
            System.out.println("中介带你看房");
        }
    
        //签合同
        public void hetong() {
            System.out.println("签租赁合同");
        }
    
        //收中介费
        public void fare() {
            System.out.println("收中介费");
        }
    }
    

    客户端访问代理角色

    //客户
    public class Client {
        public static void main(String[] args) {
    
            //房东要出租房子
            Host host = new Host();
    
            
    
            //代理 : 找中介帮房东租房子,但是呢?代理角色一般会有一些附属操作!
            Proxy proxy = new Proxy(host);
    
            //客户不用面对房东,直接找中介租房即可!
            proxy.rent();
        }
    }
    

代理模式的好处:

可以使真实角色(如房东)的操作更加纯粹!不用去关注一些公共的业务(如带他们去看房子)公共角色就交给代理角色(如中介)!实现了业务的分工!公共业务发生扩展的时候,方便集中管理!

缺点:

一个真实角色就会产生一个代理角色,代码量会翻倍,开发效率会变低~ 11.2 加深理解

代码步骤:代码对应08-demo02;

    接口

    public interface UserService {
        public void add();
        public void delete();
        public void update();
        public void query();
    }
    

    真实角色

    //真实角色
    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("查询了一个用户!");
        }
    }
    

    代理角色

    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("[Debug] 使用了一个"+msg+"方法");
        }
    }
    

    客户端访问代理角色

    public class Client {
        public static void main(String[] args) {
            UserServiceImpl userService = new UserServiceImpl();
    
            UserServiceProxy proxy = new UserServiceProxy();
            proxy.setUserService(userService);
    
            proxy.delete();
        }
    }
    

聊聊AOP:在不破坏原有代码(图左)的基础上,利用“织入”的模式来实现代码的动态配置。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-FFMB6iVp-1646488562918)(D:学习网课学习4_Spring狂神笔记图片9.png)]

11.3 动态代理

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

动态代理的代理类是动态生成的,不是我们直接写好的!

动态代理分为两大类:基于接口的动态代理,基于类的动态代理

基于接口 — JDK动态代理【我们在这里使用】基于类:cglibjava字节码实现:javassist

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

11.3.1 Proxy

它提供了动态代理类和实例的静态方法。即这个类提供了静态方法(newProxyInstance方法),这个静态方法可以去new一个动态代理实例;

该类下的方法:

Proxy.newProxyInstance(ClassLoader loader , 类[] interfaces , InvocationHandler h)

    作用:创建动态代理类,并返回该类对象。其中,每个代理实例都有一个关联的调用处理程序(即实现InvocationHandler接口的实现类), 这个实现类中的 invoke方法是proxy代理对象的实际调用处理器。参数:

    第一个参数:用类加载器来定义代理类,即用哪个类加载器去加载代理对象。第二个参数:代理类所实现的接口,支持多个接口。第三个参数:实现这个接口InvocationHandler来创建调用处理器。

11.3.2 InvocationHandler接口(调用处理器)
    它有一个唯一的方法invoke方法。即InvocationHandler接口是用来调用处理程序并返回结果的!实现这个接口来作为代理对象的调用处理器(假设Handler)。他是用来生成动态代理实例的!每当代理对象proxy被反射机制用于调用其方法时,这个方法调用就自动转移为Handler类里invoke方法体中的对应扩展方法调用。

该接口实现类下的方法:

invoke(Object proxy, Method method, Object[] args)

    作用:

    处理代理实例上的方法调用,并返回结果。所返回的结果是通过method.invoke(target , args)进行实现。它是对所有 被代理对象 的方法进行逻辑实现和扩展。 参数:

    proxy - 调用该方法的代理实例,只是反射机制调用方法的需要,意思就是你要代理谁method - 所述方法对应与调用代理实例上的接口方法的实例。意思是proxy被反射机制用于调用的方法对象,即所要代理实例的哪一个方法args - 调用方法的参数列表

11.3.3 动态代理示例

代码步骤:

    接口

    public interface Rent {
        public void rent();
    }
    

    真实角色

    public class Host implements Rent{
        public void rent() {
            System.out.println("房东要出租房子!");
        }
    }
    

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

    测试

    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就是动态生成的,我们并没有写
            proxy.rent();
        }
    }
    
    
11.3.4 动态代理万能模板

在此,我们可以提炼出ProxyInvocationHandler作为工具类

    ProxyInvocationHandler类

    //等我们用这个类,自动生成代理类!
    public class ProxyInvocationHandler implements InvocationHandler {
    
        //被代理的接口
        private Object target;
    
        public void setTarget(Object target) {
            this.target = target;
        }
    
        //返回指定接口的代理类的实例,即生成得到代理对象
        public Object getProxy(){
            //this是指被new出来的该类实例对象,getClass()是指获得该实例的类对象,类对象里的确切个体就叫实例对象
            //getClassLoader()是指获取该实例对象的类对象的类加载器
            return Proxy.newProxyInstance(this.getClass().getClassLoader(),target.getClass().getInterfaces(),this);
        }
    
    
    
        //处理代理实例,并返回结果
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    
            log(method.getName());
            //动态代理的本质,就是使用反射机制实现!
    
            //方法之前
            System.out.println("方法之前执行"+method.getName()+" :传递的参数"+ Arrays.toString(args));
    
            //被增强的方法执行
            Object result = method.invoke(target, args);
    
            //方法之后
            System.out.println("方法之后执行"+target);
    
            return result;
        }
    
        public void log(String msg) {
            System.out.println("执行了"+msg+"方法");
        }
    
    }
    

    测试

    public class Client {
    
        public static void main(String[] args) {
            //真实角色
            UserServiceImpl userService = new UserServiceImpl();
            //代理角色,不存在
            ProxyInvocationHandler pih = new ProxyInvocationHandler();
    
            pih.setTarget(userService); //设置要代理的对象
            //动态生成代理类
            UserService proxy = (UserService) pih.getProxy();
    
            proxy.add();
        }
    }
    

动态代理的好处:

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

AOP(Aspect Oriented Programming)意为:面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。AOP是OOP的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-mCyKrFT1-1646488562918)(D:学习网课学习4_Spring狂神笔记图片11.png)]

12.2 AOP在Spring中的作用

提供声明式事务;允许用户自定义切面

横切关注点:跨越应用程序多个模块的方法或功能。即是,与我们业务逻辑无关的,但是我们需要关注的部分,就是横切关注点。如日志,安全,缓存,事务等等… (理解:比如我们要加一个日志或者其他东西。是不是就是一个关注的地方?关注完之后,我们还没写!)切面(ASPECT):横切关注点被模块化的特殊对象。即,它是一个类。 (理解:现在我们要把关注的模块化成为一个类!他就叫切面)通知(Advice):切面必须要完成的工作。即,它是类中的一个方法。 (理解:然后切面里有一些方法,他就叫通知)目标(Target):被通知对象。 (理解:然后目标就是通知谁去执行呢?就是我们定义的切入点,通知在那个地方执行)代理(Proxy):向目标对象应用通知之后创建的对象。 (理解:目标和代理spring都帮忙做了,就是动态代理讲的InvocationHandler和Proxy)切入点(PointCut):切面通知执行的“地点”的定义。 (理解:在那个地方执行)连接点(JointPoint):与切入点匹配的执行点。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-YCHDrHdy-1646488562919)(D:学习网课学习4_Spring狂神笔记图片19.png)]

补充点切入点表达式:

(1) 切入点表达式作用:知道对哪个类里面的哪个方法进行增强

(2) 语法结构: [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xpkWbAWF-1646488562919)(C:UsersCyanAppDataRoamingTyporatypora-user-imagesimage-20211113162340020.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-v1fnpA9d-1646488562920)(D:学习网课学习4_Spring狂神笔记图片20.png)]

举例 1:对 com.atguigu.dao.BookDao 类里面的 add 进行增强
execution(* com.atguigu.dao.BookDao.add(..))

举例 2:对 com.atguigu.dao.BookDao 类里面的所有的方法进行增强
execution(* com.atguigu.dao.BookDao.* (..))

举例 3:对 com.atguigu.dao 包里面所有类,类里面所有方法进行增强
execution(* com.atguigu.dao.*.* (..))

SpringAOP中,通过Advice定义横切逻辑,Spring中支持5种类型的Advice:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-GQgwFUwG-1646488562920)(D:学习网课学习4_Spring狂神笔记图片13.png)]

即AOP在不改变原有代码的情况下,去增加新的功能。

12.3 使用Spring实现AOP

【重点】使用AOP织入,需要导入一个依赖包!


    org.aspectj
    aspectjweaver
    1.9.4

【拓展:】[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7JgFmlUC-1646488562921)(image-20211113233040786.png)]

方式一: 使用Spring的API接口【主要是SpringAPI接口实现】

    在service包下,定义UserService业务接口和UserServiceImpl实现类

    public interface UserService {
        public void add();
        public void delete();
        public void update();
        public void select();
    }
    
    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 select() {
            System.out.println("查询了一个用户!");
        }
    }
    

    在log包下,定义我们的增强类,一个Log前置增强和一个AfterLog后置增强类

    public class Log implements MethodBeforeAdvice {
    
        //method: 要执行的目标对象的方法
        //args:参数
        //target:目标对象
        public void before(Method method, Object[] agrs, Object target) throws Throwable {
            System.out.println(target.getClass().getName()+"的"+method.getName()+"被执行了");
        }
    }
    
    public class AfterLog implements AfterReturningAdvice {
    
        //returnValue: 返回值
        public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
            System.out.println("执行了"+method.getName()+"方法,返回结果为:"+returnValue);
        }
    }
    

    最后去spring的文件中注册 , 并实现aop切入实现 , 注意导入约束,配置applicationContext.xml文件

    
    
    
        
        
        
        
    
        
        
        
            
            
    
            
            
            
        
    
    
    

    测试

    public class MyTest {
        public static void main(String[] args) {
            ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
    
            //动态代理代理的是接口:注意点
            UserService userService = (UserService) context.getBean("userService");
    
            userService.add();
    //        userService.select();
        }
    }
    

方式二: 自定义类来实现AOP【主要是切面定义】 【狂神推荐】

缺点:功能比方式一弱,他不能获取方法里面的东西

    在diy包下定义自己的DiyPointCut切入类

    public class DiyPointCut {
        public void before(){
            System.out.println("======方法执行前======");
        }
    
        public void after(){
            System.out.println("======方法执行后======");
        }
    }
    

    去spring中配置文件

    
    
    
        
        
            
            
            
            
            
        
    
    

    测试

方式三: 使用注解实现!

    在diy包下定义注解实现的AnnotationPointCut增强类

    //声明式事务!
    //方式三 使用注解方式实现AOP
    @Aspect   //标注这个类是一个切面
    public class AnnotationPointCut {
    
        //抽取相同切入点
        @Pointcut(value = "execution(* com.cyan.service.UserServiceImpl.*(..))")
        public void pointdemo() {
        }
    
        @Before(value = "pointdemo()")
        public void before() {
            System.out.println("===========方法执行前===========");
        }
    
        @After("execution(* com.cyan.service.UserServiceImpl.*(..))")
        public void after() {
            System.out.println("===========方法执行后===========");
        }
    
        //在环绕增强中,我们可以给定一个参数,代表我们要获取处理切入的点
        @Around("execution(* com.cyan.service.UserServiceImpl.*(..))")
        public void around(ProceedingJoinPoint jp) throws Throwable {
            System.out.println("===========环绕前===========");
    
            Signature signature = jp.getSignature();//获取签名
            System.out.println("signature:"+signature);
    
            //执行方法
            Object proceed = jp.proceed();
    
            System.out.println("===========环绕后===========");
    
            System.out.println(proceed);
        }
    }
    

    在Spring配置文件中,注册bean,并增加支持注解的配置。

    
    
    
    

    测试

13、整合Mybatis

步骤:

    导入相关jar包

    junitmybatismysql数据库spring相关aop织入器mybatis-spring整合包【重点】在此还导入了lombok包。配置Maven静态资源过滤问题!

    
        
            junit
            junit
            4.12
        
        
            org.mybatis
            mybatis
            3.5.6
        
        
            mysql
            mysql-connector-java
            5.1.47
        
        
            org.springframework
            spring-webmvc
            5.2.0.RELEASE
        
        
        
        
            org.springframework
            spring-jdbc
            5.2.0.RELEASE
        
        
            org.aspectj
            aspectjweaver
            1.9.4
        
        
        
            org.mybatis
            mybatis-spring
            2.0.2
        
        
            org.projectlombok
            lombok
            1.18.10
        
    
    
    
        
            
                src/main/resources
                
                    ***.xml
                
                true
            
            
                src/main/java
                
                    ***.xml
                
                true
            
        
    
    
    
    
        UTF-8
        UTF-8
    
    

    编写配置文件

    测试

13.1 回忆mybatis

    编写pojo实体类

    @Data
    public class User {
        private int id;
        private String name;
        private String pwd;
    }
    

    编写实现mybatis的配置文件

    
    
    
    
       
           
       
    
       
           
               
               
                   
                   
                   
                   
               
           
       
    
       
           
       
    
    

    编写UserMapper接口

    public interface UserMapper {
        public List selectUser();
    }
    

    编写UserMapper.xml文件