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

Spring第两天学习笔记

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

Spring第两天学习笔记

org.springframework spring-webmvc 5.3.9 1、spring 1.1、简介
  • spring:春天 ===>给软件行业带来了春天

  • 2002年,首次推出了spring框架的雏形:interface21

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

  • [Rod Johnson](https://baike.baidu.com/item/Rod Johnson/1423612),Spring Framework创始人,著名作者,它是悉尼大学音乐学的博士

  • Spring是面向切面编程(AOP)和控制反转(IoC)的容器框架。

  • spring设计理念:使现有的技术更加容易使用,本社是一个大杂烩,整合了现有的技术框架

  • SSH:struct2 + spring + hibernate

  • SSM:springmvc + spring + mybatis

官网:https://spring.io/projects/spring-framework

官方下载地址:https://repo.spring.io/ui/native/release/org/springframework/spring

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



    org.springframework
    spring-webmvc
    5.3.19




    org.springframework
    spring-jdbc
    5.3.19

导webmvc,maven就会帮我们导入其它的所依赖的包

1.2、优点
  • spring是一个开源的免费的框架(容器)
  • spring是一个轻量级的、非入侵式的框架
  • 两大特性:控制反转(IOC)、面向切面编程(AOP)
  • 支持事务的处理,对框架整合的支持

总结:spring就是一个轻量级的控制反转和面向切面编程的框架

1.3、组成

1.4、拓展

在spring的官网有这个介绍:现代化的Java开发,就是基于spring的开发

  • Spring Boot

    • 一个快速开发的脚手架;

    • 基于Spring Boot可以快速开发单个微服务;

    • 约定大于配置。

  • Spring Cloud

    • Spring Cloud是基于Spring Boot实现的;

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

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

2、IOC理论推导

原来业务的步骤:

  1. UserDao 接口
  2. UserDaoImpl 实现类
  3. UserService 业务接口
  4. UserServiceImpl 业务实现类

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

已经发生了革命性的变化!

public class UserServiceImpl implements UserService{


    private UserDao userDao;
    //将new UserDaoImpl();隐藏,利用set进行动态实现值的注入  ===>  IOC控制反转
    public void setUserDao(UserDao userDao) {
        this.userDao = userDao;
    }

    @Override
    public void getUser() {
        //真正是业务层调用dao层的方法
        userDao.getUser();
    }
}
  • 之前,程序是主动创建对象,控制权在程序员手里
  • 现在,利用set进行动态实现值的注入,程序不在具有主动性,而是变成了被动的接收对象

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

2.1、IOC本质

IOC是一种思想,而依赖注入(DI)只是它的一种实现。

Spring过程:Spring容器在初始化时先读取配置文件,根据配置文件或元数据创建与组织对象存入容器中,程序使用时再从IOC容器中取出需要的对象。XML配置Bean时,Bean的定义信息和实现是分离的,而是采用注解的方式将两者合为一体,最新版的Spring已经可以零配置实现IOC,主要是因为Bean的定义信息以注解的形式定义在实现类中。

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

3、Hello Spring

实体类

public class Hello {
    private String str;

    @Override
    public String toString() {
        return "Hello{" +
                "str='" + str + ''' +
                '}';
    }

    public String getStr() {
        return str;
    }

    public void setStr(String str) {
        this.str = str;
    }
}

spring配置文件



    
    
        
    


测试类

public class MyTest {
    @Test
    public void hello(){
        //获取spring的上下文对象
        ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
        //我们的对象现在都在spring中管理了,我们要使用直接去里面取出来
        Hello hello = (Hello) context.getBean("hello");
        System.out.println(hello);
    }
}

思考问题?

  • Hello对象是谁创建的?
    hello对象是由Spring创建的.

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

这个过程就叫控制反转:
控制:谁来控制对象的创建,传统应用程序的对象是由程序本身控制创建的,使用Spring后,对象是由Spring来创建的.
反转(ioc):程序本身不创建对象,而变成被动的接收对象.
依赖注入(di):就是利用set方法来进行注入的.
IOC是一种编程思想,由主动的编程变成被动的接收.可以通过new ClassPathXmlApplicationContext()去浏览一下底层源码。

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

4、IOC创建对象的方式
  1. 默认使用无参构造创建对象

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

    • 下标赋值

      
      
          
      
      
    • 类型赋值

      
      
         
      
      
    • 参数名

      
      
         
      
      

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

5、Spring配置 5.1、别名


@Test
public void test(){
    ApplicationContext Context = new ClassPathXmlApplicationContext("beans.xml");
    //User user = (User)Context.getBean("user");
    User user = (User)Context.getBean("alias");
    user.show();
}
5.2、bean的配置


    

5.3、import

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

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

  • beans1

  • beans2

  • beans3

  • applicationContext.xml

    
    
    
    

使用的时候,直接使用总的配置即可

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

前面已说,不再赘述

6.2、set方式注入【重点】
  • 依赖注入:set注入
    • 依赖:bean对象的创建依赖于容器
    • 注入:bean对象中的所有属性由容器来注入

【环境搭建】

  1. 复杂类型

    public class Address {
        private String address;
    
        public Address(String address) {
            this.address = address;
        }
    
        public Address() {
        }
    }
    
  2. 真实测试对象

    public class Student {
        private String name;
    
        private Address address;
    
        private String[] books;
        private List hobbys;
        private Map card;
        private Set games;
        private String wife;
        private Properties info;
    }
    
  3. beans.xml

    
    
        
        
    
        
            
            
            
            
            
            
                
                    红楼梦
                    西游记
                    水浒传
                    戏三国
                
            
            
            
                
                    足球
                    篮球
                    排球
                    码码
                
            
            
            
                
                    
                    
                
            
            
            
                
                    LOL
                    王者荣耀
                
            
            
            
                
            
            
            
                
                    3120002988
                    
                    小李
                    abc123
                
            
            
        
    
    
    
  4. 测试类

    @Test
    public void test(){
        ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
    
        Student student = (Student) context.getBean("student");
    
        System.out.println("student.getName() = " + student.getName());
    }
    
6.3、其它方式注入

我们可以使用p、c命名空间注入

官方解释:

配置使用:




    
    
    
    


测试:

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

【注意】p、c命名空间不能直接使用,需要导入xml约束

6.4、bean的作用域(scopes)
ScopeDescription
singleton(Default) Scopes a single bean definition to a single object instance for each Spring IoCcontainer.
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 beandefinition. Only valid in the context of a web-aware SpringApplicationContext.
sessionScopes a single bean definition to the lifecycle of an HTTPSession. Only valid inthe context of a web-aware SpringApplicationContext.
applicationScopes a single bean definition to the lifecycle of aServletContext. Only valid inthe context of a web-aware SpringApplicationContext.
websocketScopes a single bean definition to the lifecycle of aWebSocket. Only valid inthe context of a web-aware SpringApplicationContext.
  1. 单例模式[singleton](Spring的默认机制)

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

    
    
  3. 剩下的request、session、application。只能在web开发中使用到

7、bean的自动装配
  • 自动装配时Spring满足bean依赖的一种方式;

  • Spring会在上下文中自动寻找,并给bean装配属性

    在Spring中有三种装配方式

  1. 在xml中显示地配置
  2. 在java中显示配置
  3. 隐式地自动装配bean【重要】
7.1、测试
  1. 环境搭建:一个人有两个宠物
7.2、byName自动装配

    
        
    
7.3、byType自动装配


 
        
    

【小结】

  • 在byName的时候,必须保证所有bean的id唯一,且要和自动注入的属性的set方法后的值一致
  • 在byType的时候,必须保证所有bean的class唯一,且要和自动注入的属性的类型一致
7.4、注解实现自动装配

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

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

使用注解之前必做:

  1. 导入约束:context约束

  2. 配置注解的支持:【重点】

    
    
    	
        
        
    
    
    

    @Autowired

    直接在用在复合类的属性(或相应的set方法)上即可,使用注解甚至不需要写set方法,因为注解是用反射来实现的。

    @Data
    public class Person {
        private String name;
        @Autowired
        private Cat cat;
        @Autowired
        private Dog dog;
    }
    

    使用@Autowired,我们可以不用编写set方法了,前提是你这个自动装配的属性在IOC(Spring)容器中存在,且符合相应的名字byName。

    常识:

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

    测试代码:

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

    如果@Autowired,自动装配的环境比较复杂,自动装配无法通过一个注解(@Autowired)完成的时候,我们可以使用@Qualifier(“value”)取配合它一起使用,指定一个唯一的bean对象的注入

    @Data
    public class Person {
        private String name;
    
        @Autowired
        @Qualifier("cat")
        private Cat cat;
        @Autowired
        private Dog dog;
    }
    

    @Resource注解

    @Data
    public class Person {
        private String name;
        @Resource(name = "cat")
        private Cat cat;
        @Resource
        private Dog dog;
    }
    

【小结】@Resource和@Autowired的区别

  • 都是用来自动装配的,都可以放在属性字段上
  • @Autowired 通过byType的方式实现,而且必须要求这个对象存在【常用】
  • @Resource 默认通过byName的方式实现,如果找不到名字,则用byType实现,若两个都找不到,则报错【常用】
  • 执行顺序不同,@Autowired 通过byType的方式实现,类型-名字。@Resource 默认通过byName的方式实现,名字-类型
8、使用注解开发

在Spring 4之后,如果要使用注解开发,必须要先保证AOP的包已经导入

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




    
    


  1. bean

  2. 属性如何注入

    @Component //等价于  
    public class User {
        //相当于     
        @Value("一北宝贝")//优先级更高
        public String name="一北";
    
    }
    

    其中@Value也可以放在相应的set方法上

  3. 衍生的注解 – 都会将修饰的类注册到IOC容器里面

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

    • dao 【@Repository】

    • service 【@Service】

    • controller 【@Controller】

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

  4. 自动装配

    • @Autowired:自动装配类型,名字
      如果Autowired不能唯一自动装配上属性,则需要通过@Qualifier(“value”)
    • @Resource:自动装配名字,类型
    • @Nullable: 若一个字段标记了该注解,则这个字段可以为null
  5. 作用域

    @Component //等价于  
    @Scope("prototype")
    public class User {
        //相当于     
        @Value("一北宝贝")//优先级更高
        public String name="一北";
    
    }
    
  6. 小结

    xml与注解:

    • xml具有普适性,适用于任何场合,维护简单方便;
    • 注解不是自己的类使用不了,维护相对复杂;

    xml与注解最佳搭配:

    • xml用来管理bean;

    • 注解只负责完成属性的注入;

    • 我们在使用的过程中,只需要注意一个问题,若要让注解生效,就需要开启注解的支持,即下面两句话

      
      
      
      
      
9、 使用Java的方式配置Spring

我们现在可以完全不使用Spring的xml配置了,全部交给Java来做

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

  • 实体类
@Data
//这个注解就是说明这个类被Spring接管了,已注册容器中
@Component
public class User {
    private String name;

    public String getName() {
        return name;
    }
    @Value("一北宝贝")//属性注入值
    public void setName(String name) {
        this.name = name;
    }
}
  • 配置类
@Configuration 
//这个同样会被Spring容器托管,因为它本质上也是一个@Component
//@Configuration代表这个类是一个配置类,和我们之前看的beans.xml等价
@ComponentScan("com.bei.pojo")
@Import(BeiConfig2.class)
public class BeiConfig {
    //注册一个bean,等价于bean标签
    //方法名等价于bean标签中的id属性
    //方法的返回值等价于bean标签中的class属性
    @Bean
    public User myUser(){
        return new User();
    }
}
  • 测试类
public class MyTest {
    @Test
    public void test(){
        //若完全使用注解(配置类)的方式去做,我们就只能通过AnnotationConfig 上下文来获取容器,通过配置类的class对象加载
        ApplicationContext context = new AnnotationConfigApplicationContext(BeiConfig.class);

        User user = context.getBean("myUser",User.class);
        System.out.println(user.getName());
    }
}

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

10、代理模式

为什么需要学习代理模式?

因为这就是SpringAOP的底层!【SpringAOP 和 SpringMVC】

代理模式的分类:

  • 静态代理:
  • 动态代理:

10.1、静态代理

角色分析:

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

代码实现:

  1. 抽象接口:

    public interface Rent {
        public void rent();
    }
    
  2. 真实角色:

    public class Host implements Rent{
        private String name;
    
        public Host(String name) {
            this.name = name;
        }
    
        @Override
        public void rent() {
            System.out.println("房东"+name+"出租房子");
        }
    }
    
  3. 代理角色:

    public class Proxy implements Rent{
        private Host host;
        
        public Proxy(Host host) {
            this.host = host;
        }
    
        @Override
        public void rent() {
            System.out.println("代理人操作**************");
            host.rent();
            System.out.println("**********************");
        }
        public void showHouse(){
            System.out.println("中介带你看房");
        }
    }
    
  4. 客户访问代理角色:

    public class Client {
        public static void main(String[] args) {
            Proxy proxy = new Proxy(new Host("贝贝"));
            proxy.showHouse();
            proxy.rent();
        }
    }
    
  5. 测试结果:

代理模式的好处:

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

代理模式的缺点:

  • 一个真实角色就会产生一个代理角色,代码量翻倍;但也不一定,有可能多个真实角色由同一个代理人代理
10.2、加深代理模式的理解

聊聊AOP:

代码

  1. 抽象接口:

    public interface UserService {
        public void add();
        public void del();
        public void update();
        public void query();
    }
    
  2. 真实类

    public class UserServiceImpl implements UserService{
    
        @Override
        public void add() {
            System.out.println("增加了一个用户");
        }
    
        @Override
        public void del() {
            System.out.println("删除了一个用户");
        }
    
        @Override
        public void update() {
            System.out.println("修改了一个用户");
        }
    
        @Override
        public void query() {
            System.out.println("查询了一个用户");
        }
    }
    
  3. 代理类

    public class UserServiceProxy implements UserService{
        private UserServiceImpl userService;
    
        //通过set一个组合类的方法来实现代理
        public void setUserService(UserServiceImpl userService) {
            this.userService = userService;
        }
    
        @Override
        public void add() {
            log("add");
            userService.add();
        }
    
        @Override
        public void del() {
            log("del");
            userService.del();
        }
    
        @Override
        public void update() {
            log("update");
            userService.update();
        }
    
        @Override
        public void query() {
            log("query");
            userService.query();
        }
        //日志方法
        public void log(String fun){
            System.out.println("[debug] 使用了"+fun+"方法");
        }
    }
    
  4. 客户访问代理类

    public class Client {
        public static void main(String[] args) {
            //创建一个真实类
            UserServiceImpl userService = new UserServiceImpl();
            //创建一个代理类
            UserServiceProxy userServiceProxy = new UserServiceProxy();
            //中介准备代理房东
            userServiceProxy.setUserService(userService);
            //中介实现代理后执行业务
            userServiceProxy.add();
        }
    }
    
10.3、动态代理
  • 动态代理和静态代理角色都一样
  • 动态代理的代理类是动态生成的,不是我们直接写好的
  • 动态代理分为两大类:基于接口的动态代理;基于类的动态代理
    • 基于接口: ——JDK 动态代理 【我们在这里使用】
    • 基于类:cglib
    • java字节码实现:javasist

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

通用万能动态代理代码实现:

  1. 抽象接口:

    public interface UserService {
        public void add();
        public void del();
        public void update();
        public void query();
    }
    
  2. 真实角色:

    public class UserServiceImpl implements UserService{
    
        @Override
        public void add() {
            System.out.println("增加了一个用户");
        }
    
        @Override
        public void del() {
            System.out.println("删除了一个用户");
        }
    
        @Override
        public void update() {
            System.out.println("修改了一个用户");
        }
    
        @Override
        public void query() {
            System.out.println("查询了一个用户");
        }
    }
    
  3. 调用处理程序类,用其生成代理类:

    //用这个类自动生成代理类
    public class ProxyInvocationHandler implements InvocationHandler {
    
        //被代理的接口
        private Object target;
    
        public void setTarget(Object target) {
            this.target = target;
        }
    
        //得到代理类
        public Object getProxy() {
                Object o = Proxy.newProxyInstance(this.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
            return o;
        }
    
        @Override//处理代理实例,并返回结果
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            log(method.getName());
            //动态代理的本质,就是使用反射的机制来实现的
            Object o = method.invoke(target, args);
            return o;
        }
        public void log(String msg){
            System.out.println("执行了"+msg+"方法");
        }
    }
    
  4. 客户访问代理角色:

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

通用动态代理类的具体化:

  1. 抽象接口:

    public interface Rent {
        public void rent();
    }
    
  2. 真实角色:

    public class Host implements Rent {
        private String name;
    
        public Host(String name) {
            this.name = name;
        }
    
        @Override
        public void rent() {
            System.out.println("房东"+name+"出租房子");
        }
    }
    
  3. 调用处理程序类,用其生成代理类:

    //用这个类自动生成代理类
    public class ProxyInvocationHandler implements InvocationHandler {
    
        //被代理的接口
        private Rent rent;
    
        public void setRent(Rent rent) {
            System.out.println("setRent方法");
            this.rent = rent;
        }
    
        //得到代理类
        public Object getProxy() {
            System.out.println("getProxy方法");
                Object o = Proxy.newProxyInstance(this.getClass().getClassLoader(), rent.getClass().getInterfaces(), this);
            return o;
        }
    
        @Override//处理代理实例,并返回结果
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            System.out.println("invoke方法");
            seeHouse();
            //动态代理的本质,就是使用反射的机制来实现的
            Object o = method.invoke(rent, args);
            return o;
        }
        public void seeHouse(){
            System.out.println("中介带客户看房子");
        }
    }
    
  4. 客户访问代理角色:

    public class Client {
        public static void main(String[] args) {
            //真实角色
            Host host = new Host("贝贝宝贝");
            //代理角色:现在没有
            //用代理调用处理程序生成一个代理类
            ProxyInvocationHandler proxyInvocationHandler = new ProxyInvocationHandler();
            //通过调用处理程序角色来处理我们要调用的接口对象
            proxyInvocationHandler.setRent(host);//代理真实角色
    
            //这里的proxy代理实例就是动态生成的,我们并没有写
            Rent proxy = (Rent) proxyInvocationHandler.getProxy();
    
           
            proxy.rent();
    
        }
    }
    

动态代理的好处:

  • 可以使真实角色的操作更加纯粹,不用去关注一些公共的业务;
  • 公共的业务交给了代理角色!实现了业务的分工;
  • 公共业务发生拓展的时候,方便集中管理;
  • 一个动态代理类代理的是一个接口,对应的是一类业务;
  • 一个动态代理类可以代理多个类,只要是实现了同一个接口即可。
转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/866928.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号