Spring框架学习笔记

spring的核心模块

spring-core依赖注入IOC与DI的最基本实现
spring-beansBean工厂与bean的装配
spring-contextspring的context上下文即IoC容器
spring-expressionspring表达式语言

spring中的IOC

     IOC是 Inverse of Control 的简写,意思是控制反转。是降低对象之间的耦合关系的设计思想,即IoC 不是一种技术,只是一种思想,一个重要的面向对象编程的法则,它能指导我们如何设计出松耦合、更优良的程序通过对对象的统一管理与分配,降低对象之间的耦合。
     Ioc意味着将你设计好的对象交给容器控制,而不是传统的在你的对象内部直接控制,也就是说在传统应用程序都是由我们在类内部主动创建依赖对象,从而导致类与类之间高耦合,难于测试;有了IoC容器后,把创建和查找依赖对象的控制权交给了容器,由容器进行注入组合对象,所以对象与对象之间是松散耦合,这样也方便测试,利于功能复用,更重要的是使得程序的整个体系结构变得非常灵活。
     下面我们通过一个maven小demo,使用代码来感受一下控制反转;我们在写web项目时,一般都是业务逻辑sevice层调用数据dao层,实现对数据的处理访问,接下来我们来简单的写写数据层和业务层。

//用户接口
public interface UserDao {

    int add();
}

//用户接口的实现类
public class UserDaoImpl implements UserDao {

    public int add() {
        System.out.println("UserDaoImpl被调用");
        return 0;
    }
}

//业务逻辑层接口
public interface UserService {

    int add();
}

//业务逻辑几口实现类
public class UserServiceImpl implements UserService {

    private UserDao userDao = new UserDaoImpl();

    public int add() {
        userDao.add();
        System.out.println("UserServiceImpl被调用");
        return 0;
    }
}

//测试类
public class Main {
    public static void main(String[] args) {
        UserService userService = new UserServiceImpl();
        userService.add();
    }
}

     运行结果:先是UserDaoImpl这个实现类被调用,然后是UserServiceImpl这个实现类被调用。
在这里插入图片描述

     以上就是我们传统开发的大致流程,这里我们有个需求就是在业务逻辑层的实现类中,假如说我们数据访问层的实现类需求有变动,实现了一个新接口UserDaoImpl2,那么我们是在代码中(业务逻辑层的实现类中)直接改动代码,修改新实现出来的对象吗?答案是否定的,由于我们在类内部主动修改对象,它是难于测试的,因为测试的一方是改不了的,测试一方可能得到的是一个字节码文件,不准许直接改源代码。所以我们就引入spring,它把对象的创建提取出来,对象的创建和修改交给IOC容器统一的进行管理分配,程序中所有的对象都通过这个容器进行管理。所以我们变更需求而修改实现类的话,直接通过配置文件修改即可(修改了配置文件就相当于修改了IOC容器),源码不能直接修改,但是配置文件可以随意修改。

使用Ioc优化代码:

  1. 在pom.xml文件中添加所需要的jar依赖
<!-- Spring的核心工具包-->
   <dependency>
     <groupId>org.springframework</groupId>
     <artifactId>spring-core</artifactId>
     <version>5.0.8.RELEASE</version>
   </dependency>
<!--在基础IOC功能上提供扩展服务,还提供许多企业级服务的支持,有邮件服务、
任务调度、远程访问、缓存以及多种视图层框架的支持-->
   <dependency>
     <groupId>org.springframework</groupId>
     <artifactId>spring-context</artifactId>
     <version>5.0.8.RELEASE</version>
   </dependency>
<!-- Spring IOC的基础实现,包含访问配置文件、创建和管理bean等 -->
   <dependency>
     <groupId>org.springframework</groupId>
     <artifactId>spring-beans</artifactId>
     <version>5.0.8.RELEASE</version>
   </dependency>
   <!-- Spring context的扩展支持,用于MVC方面 -->
   <dependency>
     <groupId>org.springframework</groupId>
     <artifactId>spring-context-support</artifactId>
     <version>5.0.8.RELEASE</version>
   </dependency>
   <!-- Spring表达式语言 -->
   <dependency>
     <groupId>org.springframework</groupId>
     <artifactId>spring-expression</artifactId>
     <version>5.0.8.RELEASE</version>
   </dependency>
  1. 创建配置文件applicationContext.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
     http://www.springframework.org/schema/beans/spring-beans.xsd">
      
</beans> 
  1. 在配置文件中创建对象
//bean表示对象   beans表示所有的对象    这个配置文件就可以理解为对对象管理的文件
<?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
     http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="userDao" class="huang.dao.impl.UserDaoImpl">
    </bean>

	
 <!--创建对象名称为userService,它是根据huang.service.impl.UserServiceImpl这个类进行创建出来的-->
    <bean id="userService" class="huang.service.impl.UserServiceImpl">
        <!--这个对象有个属性,名称是userDao,类型是huang.dao.impl.UserDaoImpl2-->
        <property name="userDao" ref="userDao"></property>
    </bean>
</beans> 

     注意这一步骤就是在使用IOC容器创建对象,对对象统一管理;在这个配置文件中对程序中所有有调用关系的对象进行管理与分配。今后在框架的学习中最好是通过这种方式创建对象,而不是直接的new。

     对象已经创建出来了,如何给程序中的对象进行赋值?
若对象是某个类的属性,那么程序就通过set方式得到对象;
若对象不是某个类的属性,就通过加载配置文件的方式得到对象。
修改UserServiceImpl中的代码,通过容器得到UserDao对象

public class UserServiceImpl implements UserService {

	//这里的userDao对象就是通过容器得到的
    private UserDao userDao;

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

    public int add() {
        userDao.add();
        System.out.println("UserServiceImpl被调用");
        return 0;
    }
}
  1. 在配置文件中创建对象
public class Main {
    public static void main(String[] args) {
        //加载配置文件
        ApplicationContext app = new ClassPathXmlApplicationContext("application.xml");
        //通过配置文件得到对象
        UserService userService = (UserService) app.getBean("userService");
        userService.add();
    }
}

     至此,程序中所有对象都是由IOC容器统一进行创建和管理的,现在我们要变更需求,换实现类只需要改动配置文件即可,如下,

<bean id="userDao" class="huang.dao.impl.UserDaoImpl2">
    </bean>
    <!--创建对象名称为userService,它是根据huang.service.impl.UserServiceImpl这个类进行创建出来的-->
    <bean id="userService" class="huang.service.impl.UserServiceImpl">
        <!--这个对象有个属性,名称是userDao,类型是huang.dao.impl.UserDaoImpl2-->
        <property name="userDao" ref="userDao"></property>
    </bean>

程序的运行结果:
在这里插入图片描述

     IOC的主要的目的就是统一创建分配对象,达到对象间的解耦。

bean标签的属性介绍

属性说明
class指定bean对象对应类的全路径
namename是bean对象对应对象的一个标识;id和name的作用都是差不多的,都是用于给bean对象起名字,但是不同的是name准许重复,而id唯一标识。
scope执行bean对象创建模式和生命周期,scope="singleton"和scope=“prototype”
idid是bean对象的唯一标识,不能添加特别字符
lazy-init是否延时加载 默认值:false。true 延迟加载对象,当对象被调用的时候才会加载,测试的时候,通过getbean()方法获得对象。lazy-init=“false” 默认值,不延迟,无论对象是否被使用,都会立即创建对象,测试时只需要加载配置文件即可。注意:测试的时候只留下id,class属性
init-method容器创建对象的初始化方法,只需要加载配置文件即可对象初始化方法
destory-method销毁对象的方法

DI依赖注入

     前面我们讲过IOC控制反转,它就是将对象统统的交给spring容器进行管理,管理完成之后就要把对象赋值给调用者,怎么赋值?这就引入了DI,其实赋值的操作就称之为DI,而依赖注入的方式有以下两种:

  • 1.set注入值
    • 基本属性类型值注入
      • property name=“name” value=“jeck”
    • 引用属性类型值注入
      • property name=“car” ref=“car”

可以参考前面小demo中的set方法注入赋值。

  • 2.构造注入
    • 通过构造方法创建对象,在创建对象的时候就通过构造函数注入赋值
public Person(String name , Car car){
  this.name = name;
  this.car = car;
  System.out.println("Person的有参构造方法:"+name+car);
 }
<bean name="person" class="com.xzk.spring.bean.Person">
    <constructor-arg name="name" value="rose"/>
    <constructor-arg name="car" ref="car"/>
</bean>
  • 通过spel spring表达式注入赋值
    + property name=“car” ref=“car”
<bean name="car" class="com.xzk.spring.bean.Car" >
     <property name="name" value="mime" />
     <property name="color" value="白色"/>
</bean>
 <!--利用spel引入car的属性 -->
 <bean name="person1" class="com.xzk.spring.bean.Person" p:car-ref="car">
 	//意思就是person1的属性name  的值是从car对象中得到的信息
     <property name="name" value="#{car.name}"/>
     <property name="age" value="#{person.age}"/>
 </bean> 
  • 通过p命名空间注入值赋值
    • 使用p:属性名 完成注入,走set方法
      • 基本类型值: p:属性名=“值”
      • 引用类型值: P:属性名-ref=“bean名称”
      • 实现步骤:配置文件中 添加命名空间p
        xmlns:p="http://www.springframework.org/schema/p"
        //实例
        <bean id="u6" class="com.entity.Users" p:age="30" p:name="李四" p:student-ref="stu1"></bean>		
        

复杂的数据类型注入

     首先创建一个类,添加复杂类型的属性,设置getter和setter方法,因为我们是通过set的方式进行注入的。

package spring01;

import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;

/**
 * @author HSD
 * @create 2020-11-20 16:50
 * 复杂类型的注入
 */
public class Teacher {
    private Object[] objects;
    private Map map;
    private List list;
    private Set set;
    private Properties properties;

    public Object[] getObjects() {
        return objects;
    }

    public void setObjects(Object[] objects) {
        this.objects = objects;
    }

    public Map getMap() {
        return map;
    }

    public void setMap(Map map) {
        this.map = map;
    }

    public List getList() {
        return list;
    }

    public void setList(List list) {
        this.list = list;
    }

    public Set getSet() {
        return set;
    }

    public void setSet(Set set) {
        this.set = set;
    }

    public Properties getProperties() {
        return properties;
    }

    public void setProperties(Properties properties) {
        this.properties = properties;
    }
}
	

在配置文件中创建对象,为对象的属性进行赋值也就是注入值。

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       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">

    <bean id="user123" class="spring01.User" p:name="hhh" p:age="22"></bean>
    <bean class="spring01.Teacher" id="teacher">
        <!--注入赋值无非就是set注入和构造注入两种,set就是通过set方法,而构造是通过构造函数-->
        //注入数组,底层其实是list
        <property name="objects">
            <list>
                <value>123</value>
                <value>王五</value>
                <ref bean="user123"></ref>
            </list>
        </property>
        //注入list集合
        <property name="list">
            <list>
                <value>123123</value>
                <value>王五122</value>
                <ref bean="user123"></ref>
            </list>
        </property>
        //注入set集合
        <property name="set">
            <set>
                <value>456456</value>
                <value>李六</value>
                <ref bean="user123"></ref>
            </set>
        </property>
        //注入map集合
        <property name="map">
            <map>
                <entry key="校花" value="谢大脚"></entry>
                <entry key="校草" value="刘能"></entry>
            </map>
        </property>
        //注入properties
        <property name="properties">
            <props>
                <prop key="name">麻子</prop>
                <prop key="age">33</prop>
            </props>
        </property>
    </bean>
</beans>
package spring01;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import java.util.*;

/**
 * @author HSD
 * @create 2020-11-20 16:49
 */
public class 复杂类型的注入 {
    public static void main(String[] args) {
    //读取配置文件中的信息
        ApplicationContext app = new ClassPathXmlApplicationContext("application2.xml");
        Teacher t = (Teacher) app.getBean("teacher");
		
		//通过对象的get方法获取值,因为对象中的属性值我们已经在配置文件中注入过了,最后循环遍历输出
        Object[] objects = t.getObjects();
        for (Object o : objects) {
            System.out.println(o);
        }

        System.out.println("---------------");
        List list = t.getList();
        for (Object o : list) {
            if (o instanceof User) {
                System.out.println(((User) o).getName() + ((User) o).getAge());
                break;
            }
            System.out.println(o);
        }
        System.out.println("---------------");
        Set set = t.getSet();
        for (Object o : set) {
            if (o instanceof User) {
                System.out.println(((User) o).getName() + ((User) o).getAge());
                break;
            }
            System.out.println(o);
        }
        System.out.println("---------------");
        Map map = t.getMap();
        Set set1 = map.keySet();
        Iterator iterator = set1.iterator();
        while (iterator.hasNext()) {
            String key = (String) iterator.next();
            System.out.println(map.get(key));
        }
        System.out.println("---------------");
        Properties properties = t.getProperties();
        System.out.println(properties.getProperty("name"));
        System.out.println(properties.getProperty("age"));
    }
}

运行结果:

在这里插入图片描述
      现在,我们所学习的spring注入DI和控制反转IOC都是通过操作配置文件的方式来实现的。也就是说我们在配置文件中创建和统一管理对象,并且用set注入和构造注入的方式给程序中对象赋值。接下来我们就来学习使用注解的方式来实现IOC。

注解实现IOC的实现步骤

  • 1.配置文件中添加约束
 <?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
      xmlns:context="http://www.springframework.org/schema/context"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xsi:schemaLocation="
      http://www.springframework.org/schema/beans
      http://www.springframework.org/schema/beans/spring-beans.xsd
      http://www.springframework.org/schema/context
      http://www.springframework.org/schema/context/spring-context.xsd
">
</beans>  
  • 2.配置注解扫描:指定扫描包下所有类中的注解,扫描包时,会扫描包所有的子孙包
    <!--扫描包的设置,扫描有相应注解的包-->
    <context:component-scan base-package="annotate.huang"></context:component-scan>
  • 3.添加注解
    • 添加在类名上,这些注解使用来创建对象的(IOC)
      • @Component(“对象名”)
        @Service(“person”) // service层
        @Controller(“person”) // controller层
        @Repository(“person”) // dao层
        @Scope(scopeName=“singleton”) //单例对象
        @Scope(scopeName=“prototype”) //多例对象
    • 添加在属性上,这些注解是用来注入依赖的(DI)
      • @Value(“属性值”)
      • @Autowired //如果一个接口类型,同时有两个实现类,则报错,此时可以借助@Qualifier(“bean name”) AutoWire默认使用的是配置文件中AutoWire的ByType类型,根据类型注入依赖,若是依赖的类型有多个的话,那么ByType就会报错,@Qualifier(“bean name”) 是AutoWire的一个补充,此注解的类型是ByName,根据名称找依赖关系,注入依赖赋值的。

我们还是使用上面讲的demo为例:

public interface UserDao {
    int add();
}

//这次我们没有在配置文件中创建对象,而是通过注解的方式创建对象
@Repository("uDao")
public class UserDaoImpl implements UserDao {

    public int add() {
        System.out.println("UserDaoImpl被调用");
        return 0;
    }
}



public interface UserService {
    int add();
}

//通过Service注解就创建了对象
@Service("userService")
public class UserServiceImpl implements UserService {

	//创建对象之后,不要忘记注入依赖
    @Autowired   //AutoWire默认使用的是配置文件中AutoWire的ByType类型,根据类型注入依赖
    @Qualifier("uDao2")  //@Qualifier("bean name")    是AutoWire的一个补充,此注解的类型是ByName,根据名称找依赖关系,注入依赖赋值的。
    private UserDao userDao;

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

    public int add() {
        userDao.add();
        System.out.println("UserServiceImpl被调用");
        return 0;
    }
}


public class Main {
    public static void main(String[] args) {
        ApplicationContext app = new ClassPathXmlApplicationContext("AnnotateApplication.xml");
        UserService userService = (UserService) app.getBean("userService");
        userService.add();
    }
}

Aop介绍

     AOP(Aspect Oriented Programming)即面向切面编程。即在不改变原程序的基础上为代码段增加新的功能。应用在权限认证、日志、事务。AOP的作用在于分离系统中的各种关注点,将核心关注点和横切关注点分离开来。

在这里插入图片描述
     JDK 的动态代理:针对实现了接口的类产生代理。实现InvocationHandler接口,以下是JDK 的动态代理的代码实现:

/**
 * @author HSD
 * @create 2020-11-21 10:24
 */
public interface UserDao {
    void Test();

    void Test2();
}

/**
 * @author HSD
 * @create 2020-11-21 10:23
 */
public class UserDaoImpl implements UserDao {
    public void Test() {
        System.out.println("UserDaoImpl");
    }

    public void Test2() {
        System.out.println("UserDaoImplTest2");
    }
}




public class JdkProxy implements InvocationHandler {

    private UserDao userDao;

    //创建有参构造,这样的话就可以在创建代理类对象的时候就可以传入一个真实userDao对象
    public JdkProxy(UserDao userDao) {
        this.userDao = userDao;
    }

    //代理方法,定义需要代理的类要做的事情
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("日志开始");

        //中间就是调取真实的方法,肯定要有一个真实的对象
        //如何执行这个真实的方法,invoke有点像反射方法
        //method就是要执行的方法   对象是userDao对象   方法中的参数是args   这就是要代理的方法执行
        //可以理解为通过反射的方法是,调取真实的方法(真实的方法进行调取)
        Object invoke = method.invoke(userDao, args);  //return 返回的值

        System.out.println("日志结束");
        return invoke;
    }
}


public class AopTest {
    public static void main(String[] args) {
        UserDao userDao = new UserDaoImpl();
        userDao.Test();

        //由代理对象来调取方法,创建代理实现类
        JdkProxy jdkProxy = new JdkProxy(userDao);
        //创建代理对象     :(要被代理的类)类加载器,接口的类型,代理的实现类
        UserDao o = (UserDao) Proxy.newProxyInstance(userDao.getClass().getClassLoader(),
                userDao.getClass().getInterfaces(), jdkProxy);
        o.Test2();
    }
}

测试结果:
在这里插入图片描述

     CGlib 的动态代理:针对没有实现接口的类产生代理,应用的是底层的字节码增强的技术 生成当前类的子类对象,MethodInterceptor接口。

public class Student {
    public void say() {
        System.out.println("我是学生");
    }

    public void say2() {
        System.out.println("我是高年级学生");
    }
}



public class CglibProxy implements MethodInterceptor {
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        System.out.println("开始日志:cglib");
        //目标方法的调用(真实的方法的调用)
        Object o1 = methodProxy.invokeSuper(o, objects);
        System.out.println("结束日志:cglib");
        return o1;
    }
}


public class CglibProxyTest {
    public static void main(String[] args) {
        //创建真实对象
        Student student = new Student();
        //创建代理对象
        Enhancer enhancer = new Enhancer();
		//指定超类的信息(真实类的信息)
        enhancer.setSuperclass(student.getClass());
        enhancer.setCallback(new CglibProxy());
        Student o = (Student) enhancer.create();
        o.say();
        o.say2();
    }
}
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值