该篇对于代码实现涉及较多

IOC

IOC即控制反转,将对象的创建以及对象之间的调用,交给spring进行管理。可以降低耦合度。

IOC的底层原理

即xml解析+工厂模式+反射

简单理解ioc过程:
image.png

IOC容器实现的两种方式(两个接口)

  1. BeanFactory:懒加载,获取或使用对象时才会去创建对象,不提供开发人员进行使用
  2. ApplictionContext:他是BeanFactory的子接口,供开发人员使用

IOC操作Bean管理

Bean的操作即创建对象、出入属性
实现这两个操作用两种方式:xml解析、注解

基于xml方式创建对象和注入属性

测试代码

 public void test() {
        ApplicationContext context = new ClassPathXmlApplicationContext("bean1.xml");

        User user = context.getBean("user", User.class);

//        ApplicationContext context=new ClassPathXmlApplicationContext("bean1.xml");
        //下面这句话体现了getBean第二个参数的作用
//        User user = (User) context.getBean("user");
        System.out.println(user);
        user.add();
    }

使用set方法注入属性

<!--      1.此方式为通过调用set实现属性注入,如类中无set方法会报错&ndash;&gt;-->
        <bean id="user" class="Spring5.x.User">
                <property name="name"  value="小名"></property>
        </bean>

使用有参构造器注入属性

需要先提供有参构造器

<bean id="user" class="Spring5.x.User">
        <constructor-arg name="name" value="构造器方法">
        </constructor-arg>

p名称空间注入

<?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="user" class="Spring5.x.User" p:name="p空间注入"></bean>

       
</beans>

注入其他类型属性

注入外部bean

beans.xml配置如下
service类和测试方法

package Spring5.x.Service;

import Spring5.x.DAO.UserDao;

public class UserService {
    UserDao userDao;

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

    public void start() {
        System.out.println("......service start");
        userDao.update();
    }
}

    @Test
    public void test2(){
        ApplicationContext context = new ClassPathXmlApplicationContext("bean2.xml");
        UserService userService = context.getBean("userService",UserService.class);
        userService.start();
    }
<?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="userService" class="Spring5.x.Service.UserService">
    <property name="userDao" ref="userDaoImpl"></property>
</bean>
    <bean id="userDaoImpl" class="Spring5.x.DAO.UserDaoImpl"></bean>
</beans>

注入内部bean与级联赋值

测试代码:

    @Test
    public void test3(){
        ApplicationContext context = new ClassPathXmlApplicationContext("bean3.xml");
        Employee employee = context.getBean("employee",Employee.class);
        System.out.println(employee);
    }
}

内部bean的xml文件配置

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
    <bean id="employee" class="Spring5.x.Bean.Employee">
        <property name="age" value="12"></property>
        <property name="name" value="劲夫"></property>
        <property name="dept" >
            <bean id="dept" class="Spring5.x.Bean.Dept">
                <property name="name" value="保安部"></property>
            </bean>
        </property>
    </bean>
</beans>

运行截图:
image.png
级联赋值的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">
<!--方式一-->
    <!--    <bean id="employee" class="Spring5.x.Bean.Employee">-->
<!--        <property name="age" value="12"></property>-->
<!--        <property name="name" value="劲夫"></property>-->
<!--        <property name="dept" ref="dept" ></property>-->
<!--    </bean>-->
<!--    <bean id="dept" class="Spring5.x.Bean.Dept">-->
<!--        <property name="name" value="保安部"></property>-->
<!--    </bean>-->
<!--方式二-->
        <bean id="employee" class="Spring5.x.Bean.Employee">
            <property name="age" value="12"></property>
            <property name="name" value="劲夫"></property>
<!--注意:这里应该添加的是Employee种的getDept()方法-->
            <property name="dept" ref="dept" ></property><!--此句不能省-->
            <property name="dept.name" value="拳师组" ></property>
        </bean>
        <bean id="dept" class="Spring5.x.Bean.Dept"></bean>
</beans>

注入集合属性

  1. 创建类,定义数组、list、map、set 类型属性,生成对应 set 方法
public class Stu {
 //1 数组类型属性
 private String[] courses;
 //2 list 集合类型属性
 private List<String> list;
 //3 map 集合类型属性
 private Map<String,String> maps;
 //4 set 集合类型属性
 private Set<String> sets;
 public void setSets(Set<String> sets) {
 this.sets = sets;
 }
 public void setCourses(String[] courses) {
 this.courses = courses;
 }
 public void setList(List<String> list) {
 this.list = list;
 }
 public void setMaps(Map<String, String> maps) {
 this.maps = maps;
 }
}
  1. 配置xml文件
<bean id="stu" class="com.atguigu.spring5.collectiontype.Stu">
 <!--数组类型属性注入-->
 <property name="courses">
 <array>
 <value>java 课程</value>
 <value>数据库课程</value>
 </array>
 </property>
 <!--list 类型属性注入-->
 <property name="list">
 <list>
 <value>张三</value>
 <value>小三</value>
 </list>
 </property>
 <!--map 类型属性注入-->
 <property name="maps">
 <map>
 <entry key="JAVA" value="java"></entry>
 <entry key="PHP" value="php"></entry>
 </map>
 </property>
 <!--set 类型属性注入-->
 <property name="sets">
 <set>
 <value>MySQL</value>
 <value>Redis</value>
 </set>
 </property>
</bean>

在集合里面设置对象类型值

<bean id="bookList" class="Spring5.x.Collection.BookList">
          
            <property name="author">
                <map>
                    <entry key="书1" value="看看"></entry>
                    <entry key="书2" value="五i"></entry>
                    <entry key="书3" value="曙光"></entry>
                </map>
            </property>
            <property name="books">
                <array>
                  <ref bean="book1"></ref>
                  <ref bean="book2"></ref>
                </array>
            </property>
        </bean>
    <bean id="book1" class="Spring5.x.Collection.Book">
        <property name="name" value="剑指Offer"></property>
    </bean>
    <bean id="book2" class="Spring5.x.Collection.Book">
        <property name="name" value="算法"></property>
    </bean>

把集合注入部分提取出来

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:util="http://www.springframework.org/schema/util"

       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd">
    <!--该配置文件中List运用的是把集合注入部分提取出来-->
    <util:list id="list"><!-- 这里记得加id-->
        <ref bean="book1"></ref>
        <ref bean="book2"></ref>
    </util:list>
    <bean id="bookList" class="Spring5.x.Collection.BookList">
            <property name="bookList" ref="list"></property>
     </bean>
    <bean id="book1" class="Spring5.x.Collection.Book">
        <property name="name" value="剑指Offer"></property>
    </bean>
    <bean id="book2" class="Spring5.x.Collection.Book">
        <property name="name" value="算法"></property>
    </bean>

将上面两例放在一起运行截图:
image.png

工厂bean

在配置文件中定义bean的类型与返回类型不一样
实现FactoryBean接口并重写方法

public class MyBean implements FactoryBean<Book> {
    int a;
    @Override
    public Book getObject() throws Exception {
        Book book =new Book();
        book.setName("<工厂类>");
        return book;
    }

    @Override
    public Class<?> getObjectType() {
        return null;
    }

    @Override
    public boolean isSingleton() {
        return false;
    }
}

bean的作用域

通过scope设置,含有四个关键词:singleton,prototype,request,session
singleton为默认,代表单例模式
prototype代表多例

bean的生命周期※

(1)通过构造器创建 bean 实例(无参数构造)
(2)为 bean 的属性设置值和对其他 bean 引用(调用 set 方法)
(3)把 bean 实例传递 bean 后置处理器的方法 postProcessBeforeInitialization
(4)调用 bean 的初始化的方法(需要进行配置初始化的方法)
(5)把 bean 实例传递 bean 后置处理器的方法 postProcessAfterInitialization
(6)bean 可以使用了(对象获取到了)
(7)当容器关闭时候,调用 bean 的销毁的方法(需要进行配置销毁的方法)
编码测试:

public class Dept {
    public String name;

     public Dept() {
        System.out.println("1.调用无参构造器创建bean实例");

    }

    public void setName(String name) {
        System.out.println("2.为bean的属性设置值和引入其他bean");
        this.name = name;
    }

     public void initBean(){
        System.out.println("4.执行初始化方法");

    }
    public void destoryBean(){
        System.out.println("7.执行销毁方法");

    }

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

//后置处理器
public class MyBean implements BeanPostProcessor {

    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {

        System.out.println("3.把bean实例传递给bean的后置处理器方法postProcessBeforeInitialization");
        return bean;
    }


    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("5.把bean实例传递给bean的后置处理器方法postProcessAfterInitialization");
        return bean;
    }

}
 //bean的生命周期
    @Test
    public void test7(){
        ApplicationContext context = new ClassPathXmlApplicationContext("bean4.xml");
        Dept dept = context.getBean("dept", Dept.class);
        System.out.println("6.获得bean实例了");
        System.out.println(dept);
        ((ClassPathXmlApplicationContext)context).close();
    }

配置文件

    <bean id="dept" class="Spring5.x.Bean.Dept" init-method="initBean" destroy-method="destoryBean">
        <property name="name" value="愁"></property>
    </bean>
<!--配置后置处理器(配置的bean都会经后置处理器处理)-->
    <bean id="mybean" class="Spring5.x.Bean.MyBean"></bean>
</beans>

运行结果截图:
image.png

自动装配(autowrit)

bean 标签属性 autowire,配置自动装配
autowire 属性常用两个值:
byName 根据属性名称注入 ,注入值 bean 的 id 值和类属性名称一样
byType 根据属性类型注入

外部属性文件引入

该部分引入德鲁伊连接池测试
把properties文件引入spring配置文件中
通过引入context名称空间实现
propertie配置文件:

prop.driverClass=com.mysql.jdbc.Driver
prop.url=jdbc:mysql://localhost:3306/userDb
prop.userName=root
prop.password=root

xml配置文件:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       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"  >
    <!-- 配置连接池 -->
    <!-- DruidDataSource dataSource = new DruidDataSource(); -->
    <context:property-placeholder location="jdbc.properties"></context:property-placeholder>
    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
        <!-- dataSource.setDriverClassName("com.mysql.jdbc.Driver");
            set方法注入
        -->
        <!-- 获取properties文件内容,根据key获取,使用spring表达式获取 -->
        <property name="driverClassName" value="${prop.driverClass}"></property>
        <property name="url" value="${prop.url}"></property>
        <property name="username" value="${prop.userName}"></property>
        <property name="password" value="${prop.password}"></property>
    </bean>
</beans>

基于注解方式进行对象创建以及注入属性

目的:简化配置

基于注解创建对象

Spring 针对 Bean 管理中创建对象提供注解
(1)@Component
(2)@Service
(3)@Controller
(4)@Repository
在配置文件中引入context命名空间

<?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     ">
    <!--开启组件扫描
     1 如果扫描多个包,多个包使用逗号隔开
     2 扫描包上层目录
    -->
    <context:component-scan base-package="Spring5.x"></context:component-scan>
</beans>

在相应类上面添加注解,若不写value默认为类名首字母小写

开启组件扫描的细节配置

<!--示例 1
 use-default-filters="false" 表示现在不使用默认 filter,自己配置 filter
 context:include-filter ,设置扫描哪些内容
-->
<context:component-scan base-package="com.atguigu" use-defaultfilters="false">
 <context:include-filter type="annotation"

expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
<!--示例 2
 下面配置扫描包所有内容
 context:exclude-filter: 设置哪些内容不进行扫描
-->
<context:component-scan base-package="com.atguigu">
 <context:exclude-filter type="annotation"

expression="org.springframework.stereotype.Controller"/>
</context:component-scan>

基于注解配置属性

Spring提供四种注解用于注入属性
通过注解注入属性不需要在添加set()

  • 引用数据类型
    @Autowried:根据类型
    @Qualifier:根据名称,需要与@Autowrite搭配使用
    @Resource:既可以根据名称又可根据类型,它不是spring的jar包中提供的,而是javax提供的
  • 基本数据类型
    @Value:基本数据类型
@Repository(value = "userDaoImpl12138")
public class UserDaoImpl implements UserDao {
    @Override
    public void update(){
        System.out.println(".......update");
    }
}
@Service
public class UserService {
    @Autowired
    @Qualifier(value = "userDaoImpl12138")
    private UserDao userDao;
    @Value(value = "aaa")
    private String name;

完全注解开发

用配置类替代配置文件,一般在SpringBoot中如此操作

@Configuration
@ComponentScan(basePackages = {"Spring5.x"})
public class SpringConfig {
}

    //纯注解开发
    @Test
    public void test10(){

        ApplicationContext context = new 
AnnotationConfigApplicationContext(SpringConfig.class);
        UserService userService = context.getBean("userService", UserService.class);

        userService.start();

    }

ApplicationContext context = new
AnnotationConfigApplicationContext(SpringConfig.class);此句和前面比有所变化

AOP

AOP优点

  • 降低对象之间的耦合度
  • 提高代码复用
  • 使系统更易扩展
  • 非业务代码更加集中,便于统一管理
  • 业务代码更加简洁纯粹,不参杂其他代码

通过动态代理来实现AOP

主要是通过创建实现了InvocationHandler接口的类,来自动态的创建代理对象。
代码实现:

public class CalImpl implements Cal {
    @Override
    public int add(int a, int b) {
        System.out.println("result is"+(a+b));
        return a + b;
    }

    @Override
    public int sub(int a, int b) {
        System.out.println("result is"+(a-b));
        return a - b;
    }
}
public class MyInvocationHandler implements InvocationHandler {

    private Object object;

    public Object bind(Object object) {
        this.object = object;//关于此处为什么要接收object,是因为下面的invoke中要作为第一个参数,调用实现类中的方法
        return Proxy.newProxyInstance(object.getClass().getClassLoader(), object.getClass().getInterfaces(), this);
    }


    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("接受的参数为" + Arrays.toString(args));
        Object invoke = method.invoke(object, args);
        System.out.println("返回的结果为" + invoke);
        return invoke;
    }
}

 //动态代理实现AOP
    @Test
    public void test1(){
        Cal cal = new CalImpl();
        MyInvocationHandler mih = new MyInvocationHandler();
        Cal proxyCal =(Cal) mih.bind(cal);
        System.out.println(proxyCal.add(2, 3));
        System.out.println(proxyCal.sub(2, 3));

    }

运行截图:
image.png

Spring实现AOP(通过添加注解实现)

对于切面类和实现类都要添加@Component注解,交给Spring容器管理
添加@Aspect表示其是一个切面类
Spring对AOP进行了封装,可以通过创建切面对象,将所有的非业务代码写在切面对象里,Spring底层会自动切面类以及目标类生成一个代理对象

@Component
@Aspect
public class MyAspect {
    @Before(value="execution(public int Spring5.x.AOP.CalImpl.*(..))")
    public void before(JoinPoint joinPoint){
        //获得方法名
        String name = joinPoint.getSignature().getName();
        System.out.println(name+"传入的参数为hahaha"+Arrays.toString(joinPoint.getArgs()));
    }
    @After(value = "execution(public int Spring5.x.AOP.CalImpl.*(..))")
    public void after(JoinPoint joinPoint){
        String name = joinPoint.getSignature().getName();
        System.out.println(name+"⽅法执⾏完毕");
    }
    @AfterReturning(value = "execution(public int Spring5.x.AOP.CalImpl.add(..))",returning = "result")
    public void afterReturning(JoinPoint joinPoint,Object result){

        System.out.println(joinPoint.getSignature().getName()+"方法的结果是lalala"+result);
    }
}
    //Spring操作实现AOP
    @Test
    public void test2(){
        ApplicationContext context = new ClassPathXmlApplicationContext("aop.xml");
        Cal calImpl = (Cal) context.getBean("calImpl");
        System.out.println(calImpl.add(2,5));
        System.out.println(calImpl.sub(2,5));
    }

xml配置文件
需要先引入aop和context命名空间


<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:p="http://www.springframework.org/schema/p"
       xsi:schemaLocation="http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-4.3.xsd
 http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
 http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.3.xsd
">
    <!--  开启自动扫描  -->
    <context:component-scan base-package="Spring5.x.AOP"></context:component-scan>
    <!--  使Aspect类生效,为目标自动生成代理对象  -->
    <aop:aspectj-autoproxy></aop:aspectj-autoproxy>
</beans>

运行截图:
image.png

Q.E.D.