栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > 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:Struts + Spring + Hibernate

  • SSM:SpringMVC + Spring + Mybatis

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

官方下载地址:http://repo.spring.io/release/org/springframework/spring

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

导入maven包:




    org.springframework
    spring-webmvc
    5.3.18






    org.springframework
    spring-jdbc
    5.3.18


1.2、优点
  • Spring是一个开源的免费的框架(容器)
  • spring是一个轻量级的、非入侵式的框架(非入侵——导入之后不会对你原有的项目造成影响不可用等问题)
  • 控制反转(IOC),面向切面编程(AOP)
  • 支持事务的处理,对框架整合的支持

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

1.3、组成

1.4、拓展

Spring官网介绍:现代化的Java开发。(就是基于Spring的开发)

  • Spring Boot
    • 一个快速开发的脚手架
    • 基于SpringBoot可以快速的开发单个微服务
    • 约定大于配置
  • Spring Cloud
    • SpringCloud是基于SpringBoot实现的

学习SpringBoot的前提,需要掌握Spring及SpringMVC。承上启下

Spring弊端:发展太久之后,违背了最初(使现有的技术更加容易使用)的理念。配置十分繁琐。人称"配置地狱"

2、IOC理论推导

举例:

一个开发思路:

首先会一定一个dao层的接口:

package com.wong.dao;

public interface UserDao {
    void getUser();
}

定义一个dao层的实现类:

package com.wong.dao;

public class UserDaoImpl implements UserDao {

    @Override
    public void getUser() {
        System.out.println("默认获取用户的数据");
    }
}

然后会定义一个service层的接口:

package com.wong.service;

public interface UserService {
    void getUser();
}

定义一个service层的实现类:

package com.wong.service;

import com.wong.dao.UserDao;
import com.wong.dao.UserDaoImpl;

public class UserServiceImpl implements UserService {
	
  	//这里会进行测试,所以需要注入这个方法
    private UserDao userDao = new UserDaoImpl();

    @Override
    public void getUser() {
        userDao.getUser();
    }
}

测试:

import com.wong.service.UserService;
import com.wong.service.UserServiceImpl;

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

如果在dao层需要新加入一个实现类:

package com.wong.dao;

public class UserDaoMysqlImpl implements UserDao {
    @Override
    public void getUser() {
        System.out.println("Mysql获取用户的数据");
    }
}

那么在service调用这个方法的时候又需要重新修改注入的对象:

package com.wong.service;

import com.wong.dao.UserDao;
import com.wong.dao.UserDaoImpl;
import com.wong.dao.UserDaoMysqlImpl;

public class UserServiceImpl implements UserService {
		
  	//这里需要重新修改注入
  	//private UserDao userDao = new UserDaoImpl();
    private UserDao userDao = new UserDaoMysqlImpl();

    @Override
    public void getUser() {
        userDao.getUser();
    }
}

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

这里使用一个set接口实现:

package com.wong.service;

import com.wong.dao.UserDao;
import com.wong.dao.UserDaoImpl;
import com.wong.dao.UserDaoMysqlImpl;

public class UserServiceImpl implements UserService {

    private UserDao userDao;

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

    @Override
    public void getUser() {
        userDao.getUser();
    }
}

测试:

import com.wong.dao.UserDaoImpl;
import com.wong.dao.UserDaoMysqlImpl;
import com.wong.service.UserService;
import com.wong.service.UserServiceImpl;

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

        //((UserServiceImpl) userService).setUserDao(new UserDaoImpl());
        ((UserServiceImpl) userService).setUserDao(new UserDaoMysqlImpl());

        userService.getUser();
    }
}

使用一Set接口实现,这里已经发生了革命性的变化

private UserDao userDao;

//利用set进行动态实现值的注入
public void setUserDao(UserDao userDao) {
  this.userDao = userDao;
}
  • 之前,程序是主动创建对象。控制权在程序员手上。

    private UserDao userDao = new UserDaoImpl();
    private UserDao userDao = new UserDaoMysqlImpl()
    private UserDao userDao = new UserDaoOracleImpl()
    private UserDao userDao = new UserDaoSqlServerImpl()
    ......
    
  • 使用了set注入之后,程序不再具有主动性,而是变成了被动接受对象

    ((UserServiceImpl) userService).setUserDao(new UserDaoImpl());
    ((UserServiceImpl) userService).setUserDao(new UserDaoMysqlImpl());
    ((UserServiceImpl) userService).setUserDao(new UserDaoOracleImpl());
    ((UserServiceImpl) userService).setUserDao(new UserDaoSqlServerImpl());
    ......
    

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

IOC本质

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

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

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

3、HelloSpring 3.1、第一个Spring程序
  1. 创建一个实体:

    package com.wong.pojo;
    
    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 + ''' +
                    '}';
        }
    }
    
  2. 加入配置文件命名为beans.xml

    
    
    
        
        
            
        
    
    
    
  3. 测试

    import com.wong.service.UserServiceImpl;
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.support.ClassPathXmlApplicationContext;
    
    public class MyTest {
        public static void main(String[] args) {
            //获取Spring的上下文对象
          	//解析beans.xml文件,生成管理相应的Bean对象
            ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
            //对象现在都在Spring中管理了,要使用,直接去里面取出来就可以了
          	//getBean : 参数即为spring配置文件中bean的id
            Hello hello = (Hello) context.getBean("hello");
            System.out.println(hello.toString());
        }
    }
    
3.2、改造IOC理论推导时的例子

在配置文件中把需要的对象注册进容器:

 

如果需要修改调用另一方法,则只需要修改配置文件中的值即可:


	
  

这里说明一下name="userDao"其实就是UserServiceImpl中的那个set方法(在xml中点击可以跳转到相应的地方)

package com.wong.service;

import com.wong.dao.UserDao;

public class UserServiceImpl implements UserService {

    private UserDao userDao;

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


    public void getUser() {
        userDao.getUser();
    }
}

beans.xml配置:




    
    

    
        
        
    


测试:

import com.wong.service.UserServiceImpl;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class MyTest {
    public static void main(String[] args) {
        //获取Spring的上下文对象,拿到spring的容器
        ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
        UserServiceImpl userService = (UserServiceImpl) context.getBean("userService");
        userService.getUser();
    }
}

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

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

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

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

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

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

4、IOC创建对象的方式 4.1、原始的new方法测试
  1. 新建一个实体类

    package com.wang.pojo;
    
    public class User {
        private String name;
    
    
        //不管有没有定义,都是存在默认的无参构造的
        //这里显示的定义无参构造,查看在这个类初始化的时候打印无参构造
        public User() {
            System.out.println("User的无参构造");
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public void show() {
            System.out.println("name=" + name);
        }
    }
    
  2. 测试:

    import com.wang.pojo.User;
    
    public class MyTest {
        public static void main(String[] args) {
            User user = new User();
        }
    }
    

发现执行这个方法的时候就打印出了"User的无参构造"

4.2、使用Spring方法测试

配置beans.xml文件:



    
        
    

测试打印如下:

//User的无参构造
//name=小红

import com.wang.pojo.User;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class MyTest {
    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
        User user = (User) context.getBean("user");
        user.show();
    }
}

然后去掉实体类中的无参构造方法(添加一个有参构造的方法,不定义无参构造):

package com.wang.pojo;

public class User {
    private String name;

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

    public String getName() {
        return name;
    }

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

    public void show() {
        System.out.println("name=" + name);
    }
}

发现xml文件中就出现了报错,并且测试控制台也会报错



    
        
    


所以得出创建方式是默认的无参构造对象

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

  • 下标赋值

    
      
    
    
  • 通过类型(不建议使用),不排除有相同的类型数据

    
      
    
    
  • 参数名

    
      
    
    

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

5、Spring配置 5.1、别名

如果添加了别名,也可以使用别名获取到这个对象


  



5.2、Bean的配置

id:bean的唯一标识符,相当于对象的对象名

class:bean对象所对应的全限定名:包名 + 类型

name:也是别名,而且name可以同时取多个别名。下面描述的分隔符都能识别


  

5.3、import

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

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

  • 张三
  • 李四
  • applicationContext.xml



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

6、DI依赖注入 6.1、构造注入

IOC创建对象的方式中描述的无参构造和有参构造部分

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

【环境搭建】

  1. 复杂类型

    package com.wang.pojo;
    
    public class Address {
        private String address;
    
        public String getAddress() {
            return address;
        }
    
        public void setAddress(String address) {
            this.address = address;
        }
    }
    
  2. 真实测试对象

    package com.wang.pojo;
    
    import java.util.*;
    
    public class User {
        private String name;
        private Address address;
        private String[] books;
        private List hobbys;
        private Map card;
        private Set games;
        private Properties info;
        private String ss;
    		//get、set方法
    }
    
  3. beans.xml

    
    
        
            
        
        
            
            
            
            
            
            
                
                    西游记
                    三国演义
                    红楼梦
                
            
            
            
                
                    打篮球
                    听音乐
                
            
            
            
                
                    
                    
                
            
            
            
                
                    LOL
                    PUBG
                    DNF
                
            
            
            
                
            
            
            
                
                    20220212
                    男
                    小明
                    小明
                    小明
                
            
        
    
    
  4. 测试

    import com.wang.pojo.User;
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.support.ClassPathXmlApplicationContext;
    
    public class MyTest {
        public static void main(String[] args) {
            ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
            User user = (User) context.getBean("user");
            System.out.println(user.toString());
        }
    }
    
6.3、拓展方式注入(其他方式)

需要导入xml约束

p命名空间:


        xmlns:p="http://www.springframework.org/schema/p"
        xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd">

使用:


  
  




C命名空间:


        xmlns:c="http://www.springframework.org/schema/c"
        xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd">

使用:需要有有参和无参构造器,因为是构造器注入方式


  
  




6.4、Bean的作用域

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

    
      
      
    
    

    这里获取的"user"和"user2"都是相等的,同一个对象不管获取多少个都是为"true"

        public static void main(String[] args) {
            ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
            User user = (User) context.getBean("user");
            User user2 = context.getBean("user", User.class);
            System.out.println(user == user2);//true
        }
    

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

    
    

  1. 其余的request、session、application这些只能在Web开发中使用到
7、Bean的自动装配
  • 自动装配是Spring满足bean依赖的一种方式
  • Spring会在上下文中自动寻找,并自动给bean装配属性

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

  1. 在xml中显式的配置(上面部分)
  2. 在java中显式配置
  3. 隐式的自动装配bean
7.1、测试
  1. 环境搭建:一个人有两个宠物

    人:

    package com.wang.pojo;
    
    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 + ''' +
                    '}';
        }
    }
    

    猫和狗:

    package com.wang.pojo;
    
    public class Cat {
        public void shout() {
            System.out.println("CatCatCatCatCatCatCat");
        }
    }
    
    package com.wang.pojo;
    
    public class Dog {
        public void shout() {
            System.out.println("DogDogDogDogDogDog");
        }
    }
    

    beans.xml:

    
    
    
        
        
        
            
            
            
        
    
    

    测试:

    import com.wang.pojo.People;
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.support.ClassPathXmlApplicationContext;
    
    public class MyTest {
        public static void main(String[] args) {
            ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
            People people = context.getBean("people", People.class);
            people.getCat().shout();
            people.getDog().shout();
        }
    }
    
7.2、byName自动装配

这里bean id改成大写也会报错:

例如:






  

7.3、byType自动装配

如果有两个相同的类型也会报错,编译无法通过:这样是不行的

bean id可以没有,以为是根据类型来的






  





  

小结:

  • byname的时候,需要保证所有bean的id唯一,并且这个bean需要和自动注入的属性的set方法的值一致。
  • bytype的时候,需要保证所有bean的class唯一,并且这个bean需要和自动注入的属性的类型一致。
7.4、使用注解实现自动装配

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

要使用注解须知:

  1. 导入约束

    
    
    
    
  2. 配置注解的支持

    
    
    
    
    
    
    

@Autowired

直接在属性上使用即可,也可以在set方式上使用。

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



    

    
    
    

package com.wang.pojo;

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

public class People {
    @Autowired
    private Cat cat;
    @Autowired
    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 + ''' +
                '}';
    }
}

说明:

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

如果显示定义了Autowired 的required属性为false,说明这个对象可以为null,否则不允许为空:

@Autowired(required = false)
public @interface Autowired {
    boolean required() default true;
}

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

这种情况是在beans.xml中配置的bean的id或者class比较乱的时候

public class People {
    @Autowired(required = false)
    @Qualifier(value = "cat1111")
    private Cat cat;
    @Autowired
    @Qualifier(value = "dog111")
    private Dog dog;

    private String name;
}


    

    
    
    
    
    

@Resource注解

public class People {

    @Resource(name = "cat2")
    private Cat cat;
    @Resource
    private Dog dog;

    private String name;
}

小结:

@Resource和@Autowired的区别:

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

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

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



    



@Component:组件,放在类上,说明这个类被Spring管理了,就是bean

  1. bean

  2. 属性如何注入

    package com.wang.pojo;
    
    import org.springframework.beans.factory.annotation.Value;
    import org.springframework.stereotype.Component;
    
    //等价于  
    //@Component 组件
    @Component
    public class User {
        
        private String name;
    
        //相当于
        @Value("小明")
        public void setName(String name) {
            this.name = name;
        }
    }
    
  3. 衍生的注解

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

    • dao【@Repository】

    • service【@Service】

    • controller【@Controller】

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

  4. 自动装配

    见7.4

  5. 作用域

    与在xml中配置的一样。在这里加在类上

    //等价于  
    //@Component 组件
    @Component
    //@Scope("singleton")
    //@Scope("prototype")
    
    public class User {
    
        private String name;
    
        //相当于
        @Value("小明")
        public void setName(String name) {
            this.name = name;
        }
    }
    
  6. 小结

    xml与注解:

    • xml更加万能,适用于任何场合。维护简单方便。

    • 注解不是自己类使用不了,维护相对复杂。

    xml与注解最佳实践:

    • xml用来管理bean

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

    • 在使用过程中,需注意:让注解生效,需要开启注解的支持

      
      
      
      
9、使用Java的方式配置Spring

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

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

例子:使用Java的方式配置而不是xml的形式

实体类:

package com.wong.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("张三")//属性注入值
    public void setName(String name) {
        this.name = name;
    }

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

配置类:(代替xml)

package com.wong.config;

import com.wong.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;

@Configuration
//这个也会被Spring容器托管,注册到容器中,因为他本就就是一个@Component
//@Configuration代表这是一个配置类,就和之前的beans.xml一样的作用
@ComponentScan("com.wong.pojo")//扫描包
@Import(User.class)//引入其他的类
public class MyConfig {

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

测试:

import com.wong.config.MyConfig;
import com.wong.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(MyConfig.class);
        User getUser = (User) context.getBean("getUser");
        System.out.println(getUser.getName());
    }
}
10、代理模式

代理模式就是SpringAOP的底层

代理模式的分类:

  • 静态代理
  • 动态代理

10.1、静态代理

角色分析:

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

例子:租房举例(接口-租房、真实角色-房东、代理角色-中介、客户-租房的人)

租房:

package com.wong.demo01;

//租房接口
public interface Rent {
    void rent();
}

房东:

package com.wong.demo01;

//房东
public class Host implements Rent {

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

租客:

package com.wong.demo01;

public class Client {
    public static void main(String[] args) {
      	//租客在没有代理之前,直接接触房东
        Host host = new Host();
        host.rent();
    }
}

中介:

package com.wong.demo01;

public class Proxy implements Rent {
    private Host host;

    public Proxy() {
    }

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


    public void rent() {
        seeHouse();
        host.rent();
    }

    //在不影响原有代码的情况下,实现了功能的扩展
    //中介现在还能去做其他的事情。比如说:带你看房、手中介费
    public void seeHouse() {
        System.out.println("中介带你看房");
    }

}

添加代理对象(中介)后的租客:

package com.wong.demo01;

public class Client {
    public static void main(String[] args) {
        //房东要出租房子
        Host host = new Host();
        host.rent();
        //代理 中介帮房东租房子。可以附属其他操作,比如说带租客去看房子,签合同等操作
        Proxy proxy = new Proxy(host);
        //租客不用面对房东,直接找中介租房即可
        proxy.rent();

    }
}

代理模式的好处:

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

缺点:

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

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

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

动态代理的好处:

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

例子:

接口:

//租房接口
public interface Rent {
    void rent();
}

真实角色:

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

动态代理类:

package com.wong.demo02;

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

//用这个类 ,自动生成代理类
public class ProxyInvocationHandler implements InvocationHandler {
    //实现InvocationHandler,重写方法invoke

    //被代理的接口
    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);
        return result;
    }


    //同样可以在调用前后实现附属的操作
    public void seeHouse() {
        System.out.println("中介带你看房");
    }
}

客户测试:

package com.wong.demo02;

public class Client {
    public static void main(String[] args) {
        //真实角色
        Host host = new Host();
        //代理角色:现在还并不存在
        ProxyInvocationHandler pih = new ProxyInvocationHandler();
        //通过调用程序处理角色来处理我们要调用的接口对象
        pih.setRent(host);
        //这里的proxy就是动态生成的
        Rent proxy = (Rent) pih.getProxy();
        proxy.rent();
    }
}
11、AOP 11.1、什么是AOP

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

11.2、Aop在Spring中的作用

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

  • 横切关注点:跨越应用程序多个模块的方法或功能。即是,与我们业务逻辑无关的,但是我们需要关注的部分,就是横切关注点。如日志,安全,缓存,事务等等…
  • 切面(ASPECT):横切关注点,被模块化的特殊对象。即,它是一个类。
  • 通知(Advice) :切面必须要完成的工作。即,它是类中的一个方法。
  • 目标(Target):被通知对象。
  • 代理(Proxy):向目标对象应用通知之后创建的对象。
  • 切入点(PointCut):切面通知执行的“地点”的定义。
  • 连接点(JointPoint) :与切入点匹配的执行点。

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

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

11.3、使用Spring实现AOP

使用AOP织入,需要导入一个依赖包



    org.aspectj
    aspectjweaver
    1.9.8

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

实例:在增删改查方法前后进行日志添加

接口:

package com.wong.service;

public interface UserService {
    void add();

    void delete();

    void update();

    void select();
}

接口实现类:

package com.wong.service;

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("查询了一个用户");
    }
}

定义横切:

package com.wong.log;

import org.springframework.aop.MethodBeforeAdvice;

import java.lang.reflect.Method;

public class Log implements MethodBeforeAdvice {

    //method:要执行的目标对象的方法
    //args:参数
    //target:目标对象
    public void before(Method method, Object[] args, Object target) throws Throwable {
        System.out.println(target.getClass().getName() + "---的---" + method.getName() + "被执行了");
    }
}
package com.wong.log;

import org.springframework.aop.AfterReturningAdvice;

import java.lang.reflect.Method;

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

xml配置:




    
    
    
    

    
    
    
        
        
        
        

        
        
        
    

测试:

import com.wong.service.UserService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class MyTest {
    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
        //动态代理代理的是接口,而不是具体的实现类
        //这种写法报错:UserServiceImpl userservice = context.getBean("userservice", UserServiceImpl.class);
        UserService userservice = context.getBean("userservice", UserService.class);
        userservice.add();
    }
}

方式二:自定义来实现AOP【主要是切面定义】

自定义一个切面:

package com.wong.diy;

public class DiyPointcut {

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

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

xml配置AOP:

    
    
    
    
        
        
            
            
            
            
            
        
    

测试(与上面一样)

方式三:使用注解实现

实体类:

package com.wong.diy;

//方式三:使用注解方式实现AOP


import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;

@Aspect//标注这个类是一个切面
public class AnnotationPointCut {

    @Before("execution(* com.wong.service.UserServiceImpl.*(..))")
    public void before() {
        System.out.println("=====方法执行前=====");
    }

    @After("execution(* com.wong.service.UserServiceImpl.*(..))")
    public void after() {
        System.out.println("=====方法执行后=====");
    }

    //在环绕增加中,我们可以给定一个参数,代表我们要获取处理切入的点
    @Around("execution(* com.wong.service.UserServiceImpl.*(..))")
    public void around(ProceedingJoinPoint joinPoint) throws Throwable {
        System.out.println("=====环绕前=====");

        Signature signature = joinPoint.getSignature();//获得签名
        System.out.println("signature:" + signature);
        //执行方法
        Object proceed = joinPoint.proceed();
        System.out.println("=====环绕后=====");
        System.out.println(proceed);
    }
}

xml配置:

    
    
    
    

测试(与上面一样)

12、整合Mybatis 13、声明式事务 13.1、事务
  • 把一组业务当成一个业务来做,要么都成功,要么都失败
  • 确保数据完整性和一致性

事务的ACID原则:

  • 原子性
  • 一致性
  • 隔离性
    • 多个业务可能操作同一个资源,防止数据损坏
  • 持久性
    • 事务一旦提交,无论系统发生什么问题,结果都不会再被影响,被持久化的写到存储器中
13.2、Spring中的事务管理
  • 声明式事务:AOP
  • 编程式事务:需要在代码中,进行事务的管理

事务的传播特性有七种,可以查询了解。

为什么需要事务:

  • 如果不配置事务,可能存在数据提交不一致的情况
  • 如果不在Spring中配置声明式事务,就需要在代码中手动配置事务
转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/919745.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

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

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