赞
踩
传统开发中,需要调用对象的时候,需要调用者手动来创建被调用者的实例,即对象是由调用者new出来的;
但在Spring框架中,创建对象的工作不再由调用者来完成,而是交给IOC容器来创建,再推送给调用者,用完再还回来,类似于缓冲池,整个流程完成反转,所以是控制反转。
注:以下内容,获取DataConfig的对象是最终目的
- <dependency>
- <groupId>org.springframework</groupId>
- <artifactId>spring-context</artifactId>
- <version>5.3.15</version>
- </dependency>
@Data是可以不用手动set、get
- package com.circle.ioc;
-
- import lombok.Data;
-
- @Data
- public class DataConfig {
- private String url;
- private String driverName;
- private String username;
- private String password;
- }
- <bean class="com.circle.ioc.DataConfig" id="config">
- <property name="driverName" value="Driver"></property>
- <property name="url" value="localhost:3306"></property>
- <property name="username" value="root"></property>
- <property name="password" value="root"></property>
- </bean>
1.3.1配置文件:
bean:通过配置bean标签来完成对象的管理
id:对象名
class:对象的模板类(所有交给IOC容器来管理的类必须要有无参构造函数,因为Spring底层是通过反射机制来创建对象,调用的是无参构造)
property:对象的成员变量通过property标签完成赋值
name:成员变量名
value:成员变量值(基本数据类型,String可以直接赋值,如果是其他引用类型不可以通过value赋值)
ref:把IOC中的另一个bean赋给当前成员变量(DI依赖注入),见下图(一个类中有另一个类的对象)此时用ref,不能用value,否则会抛出类型转换异常
1.3.2 IOC容器创建bean的两种方法:
无参构造函数(需要提供对应的set方法或使用lombok):property
有参构造函数:constructor-arg
type:用于指定要注入的数据的数据类型,该数据类型也是构造函数中某个或某些参数的类型。
index:用于指定要注入的数据给构造函数中指定索引位置的参数赋值。索引的位置是从0开始。
name:用于指定给构造函数中指定名称的参数赋值。
<bean id="stu1" class="com.zyh.pojo.Student"> <constructor-arg name="id" value="1"> </constructor-arg> <constructor-arg name="name" value="李四"></constructor-arg> </bean> <bean id="stu1" class="com.zyh.pojo.Student"> <constructor-arg index=0 value="1"> </constructor-arg> <constructor-arg index=1 value="李四"></constructor-arg> </bean>
1.3.3 bean是根据scope来生成的,表示bean的作用域
singleton,单例,表示通过Spring容器获取的对象是唯一的,是默认值。只要加载IOC容器,不管是否从IOC种取出bean,配置文件中的bean都会被创建,而且只会创建一个对象
prototype,原型,表示通过Spring容器获取的对象是不同的。如果不从IOC中取出bean,则不创建对象,取一次bean,就会创建一个对象
<bean id="user" class="com.zyh.pojo.User" scope="prototype"> <property name="id" value="1"></property> <property name="name" value="张三"></property> </bean>request,请求,表示在异常HTTP请求内有效,一般用于web项目
session,会话,表示在一个用户会话内有效,一般用于web项目
- public class Student {
- private String name;
- private Person person;
- private String[] arr;
- private List<String> myList;
- private Map<String,String> myMap;
- private Set<String> mySet;
- private String wife;
- private Properties myPro;
- }
- <bean id="student" class="com.kang.pojo.Student">
- <!--普通值注入,value:具体属性值-->
- <property name="name" value="jerry"/>
-
- <!--Bean注入,ref:对象-->
- <property name="person" ref="person"/>
-
- <!--数组注入-->
- <property name="arr">
- <array>
- <value>AAA</value>
- <value>BBB</value>
- <value>CCC</value>
- </array>
- </property>
-
- <!--List注入-->
- <property name="myList">
- <list>
- <value>111</value>
- <value>222</value>
- <value>333</value>
- </list>
- </property>
-
- <!--Map注入-->
- <property name="myMap">
- <map>
- <entry key="aaa" value="aaaa"></entry>
- <entry key="bbb" value="bbbb"></entry>
- <entry key="ccc" value="cccc"></entry>
- </map>
- </property>
-
- <!--Set注入-->
- <property name="mySet">
- <set>
- <value>111</value>
- <value>222</value>
- <value>333</value>
- </set>
- </property>
-
- <!--null注入-->
- <property name="wife">
- <null/>
- </property>
-
- <!--Properties注入-->
- <property name="myPro">
- <props>
- <prop key="aaa">aaaa</prop>
- <prop key="bbb">bbbb</prop>
- <prop key="ccc">cccc</prop>
- </props>
- </property>
- </bean>
p命名空间是set注入的一种快捷实现方式,想要使用p命名空间注入,需要注意一下几点。
1. 实体类中必须有set方法;
2. 实体类中必须有无参构造器(默认存在);
3. 必须导入p命名空间注入方式依赖。(类对应的xml配置文件)
xmlns:p="http://www.springframework.org/schema/p"
4. 导入后即可使用:
<bean id="user" class="com.yd.pojo.User" p:age="18" p:name="老王"/>
c命名空间是构造器注入的一种快捷实现方式,想要使用c命名空间,需要注意一下几点。
1. 实体类中必须存在有参构造器;
2. 必须导入c命名空间注入方式依赖。
xmlns:c="http://www.springframework.org/schema/c"
3. 导入后即可使用:
<bean id="user2" class="com.yd.pojo.User" c:age="23" c:name="中王"/>
- package com.circle.ioc;
-
- import org.springframework.context.ApplicationContext;
- import org.springframework.context.support.ClassPathXmlApplicationContext;
-
- import javax.xml.crypto.Data;
-
- public class Test {
- public static void main(String[] args) {
- // // 不用ioc,所有对象开发者自己创建
- // DataConfig dataConfig = new DataConfig();
- // dataConfig.setDriverName("Driver");
- // dataConfig.setUrl("localhost::3306/dbname");
- // dataConfig.setUsername("root");
- // dataConfig.setPassword("root");
-
- // 使用ioc,对象不用开发者创建,交给spring框架完成
- // 两种方式:基于注解和XML(bean),主要是注解
- // 基于xml:把需要的对象在xml中进行配置,spring框架读取这个配置文件,根据配置文件的内容和反射机制来创建对象
- ApplicationContext context = new ClassPathXmlApplicationContext("spring.xml"); // ioc容器
- System.out.println(context.getBean("config")); // 从ioc中取出bean,通过id
- }
- }
1.4.1从IOC容器中取bean的方法:
通过id取值:
Student stu = (Student)applicationContext.getBean("stu");
通过类型取值
Student stu = applicationContext.getBean(Student.class);
当IOC容器中存在两个以上Student Bean的时候就会抛出异常,因为此时没有唯一的bean
静态工厂类:
- 创建一个目标类对应的静态工厂类,写一个静态方法
- 在目标类对应的配置文件中
<?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="car" class="com.zyh.factory.StaticCarFactory" factory-method="getCar"> <constructor-arg name="num" value="1"></constructor-arg> </bean> </beans>factory-method 指向静态方法
constructor-arg的name、value属性是调用静态方法传入的参数
- 静态工厂方法创建对象,不需要实例化工厂对象,因为静态工厂的静态方法,不需要创建对象就可以调用了
实例工厂类:
- 创建一个目标类对应的实例工厂类,写一个方法
- 在目标类对应的配置文件中
<!-- 实例工厂类--> <bean id="instanceCarFactory" class="com.zyh.factory.InstanceCarFactory"></bean> <!-- 通过实例工厂获取Car--> <bean id="car1" factory-bean="instanceCarFactory" factory-method="getCar"> <constructor-arg value="2"></constructor-arg> </bean>
- 实例工厂方法创建对象,需要实例化工厂对象,因为方法是非静态的,就必须通过实例化对象才能调用,所以必须创建工厂对象,spring.xml需要配置两个bean,一个是工厂类bean,一个是方法Bean
在类前加@Configuration即为配置类,整个配置类可以类比为xml文件;在类中写一个方法,方法前加@Bean,返回一个对象存入IOC容器中,类比为bean。
- package com.circle.configuration;
-
- import com.circle.ioc.DataConfig;
- import org.springframework.context.annotation.Bean;
- import org.springframework.context.annotation.Configuration;
-
- // 基于注解
- // 1. 配置类:用一个java类替代xml文件,把在xml中配置的内容放在配置类中
- // 2. 扫包 + 注解
- @Configuration // 配置类
- public class BeanConfiguration {
- // 整个BeanConfiguration类对应spring.xml文件
- // 方法返回的对象对应xml中的bean
- // 加载配置类时需调用这个方法,把返回的对象存入ioc
- // value或name取别名,但不能再使用原方法名了,id唯一
- @Bean(value = "config")
- public DataConfig dataConfig(){
- DataConfig dataConfig = new DataConfig();
- dataConfig.setDriverName("Driver");
- dataConfig.setUrl("localhost::3306/dbname");
- dataConfig.setUsername("root");
- dataConfig.setPassword("root");
- return dataConfig;
- }
- }
- package com.circle.ioc;
-
- import com.circle.configuration.BeanConfiguration;
- import org.springframework.context.ApplicationContext;
- import org.springframework.context.annotation.AnnotationConfigApplicationContext;
- import org.springframework.context.support.ClassPathXmlApplicationContext;
-
- import javax.xml.crypto.Data;
-
- public class Test {
- public static void main(String[] args) {
- // 基于注解:配置类
- // 读取配置类初始化IOC容器
- ApplicationContext context = new AnnotationConfigApplicationContext(BeanConfiguration.class);
- // System.out.println(context.getBean("dataConfig")); // 方法名作为id
- System.out.println(context.getBean("config")); // @Bean后加上value或name赋值,也可用value的值作为id,但取了value后,不能再用原方法名了,因为只有一个id
- }
- }
- package com.circle.ioc;
-
- import com.circle.configuration.BeanConfiguration;
- import org.springframework.context.ApplicationContext;
- import org.springframework.context.annotation.AnnotationConfigApplicationContext;
- import org.springframework.context.support.ClassPathXmlApplicationContext;
-
- import javax.xml.crypto.Data;
-
- public class Test {
- public static void main(String[] args) {
- // 扫包:配置类的包
- ApplicationContext context = new AnnotationConfigApplicationContext("com.circle.configuration");
- System.out.println(context.getBean("dataConfig"));
- }
- }
@Component注解告诉Spring框架,这个类需要创建对象注入到IOC容器
@value注解赋值
- package com.circle.ioc;
-
- import lombok.Data;
- import org.springframework.beans.factory.annotation.Value;
- import org.springframework.stereotype.Component;
-
- @Data
- @Component
- public class DataConfig {
- @Value("localhost:3306")
- private String url;
- @Value("Drive")
- private String driverName;
- @Value("root")
- private String username;
- @Value("root")
- private String password;
- }
- package com.circle.ioc;
-
- import com.circle.configuration.BeanConfiguration;
- import org.springframework.context.ApplicationContext;
- import org.springframework.context.annotation.AnnotationConfigApplicationContext;
- import org.springframework.context.support.ClassPathXmlApplicationContext;
-
- import javax.xml.crypto.Data;
-
- public class Test {
- public static void main(String[] args) {
- // 扫包:目标类的包
- ApplicationContext context = new AnnotationConfigApplicationContext("com.circle.ioc");
- System.out.println(context.getBean(DataConfig.class));
- }
- }
1. @Autowired 自动装载注解,自动去ioc中找DataConfig类型的Bean,默认通过类型注入(ByType),找到了就拿过来赋值(DataConfig类也一定要有@Component注解)。
2. 如果想通过名字注入(ByName),使用@Qualifier注解
- package com.circle.ioc;
-
- import lombok.Data;
- import org.springframework.beans.factory.annotation.Autowired;
- import org.springframework.beans.factory.annotation.Value;
- import org.springframework.stereotype.Component;
-
- import java.security.SecureRandom;
- @Data
- @Component
- public class GlobalConfig {
- @Value("8080")
- private String port;
- @Value("/")
- private String path;
- @Autowired
- private DataConfig dataConfig;
- }
- package com.circle.ioc;
-
- import lombok.Data;
- import org.springframework.beans.factory.annotation.Value;
- import org.springframework.stereotype.Component;
-
- @Data
- @Component
- public class DataConfig {
- @Value("localhost:3306")
- private String url;
- @Value("Drive")
- private String driverName;
- @Value("root")
- private String username;
- @Value("root")
- private String password;
- }
- package com.circle.ioc;
-
- import com.circle.configuration.BeanConfiguration;
- import org.springframework.context.ApplicationContext;
- import org.springframework.context.annotation.AnnotationConfigApplicationContext;
- import org.springframework.context.support.ClassPathXmlApplicationContext;
-
- import javax.xml.crypto.Data;
-
- public class Test {
- public static void main(String[] args) {
- // 扫包:目标类的包
- ApplicationContext context = new AnnotationConfigApplicationContext("com.circle.ioc");
- System.out.println(context.getBean(DataConfig.class));
- }
- }
- package com.circle.ioc;
-
- import lombok.Data;
- import org.springframework.beans.factory.annotation.Autowired;
- import org.springframework.beans.factory.annotation.Qualifier;
- import org.springframework.beans.factory.annotation.Value;
- import org.springframework.stereotype.Component;
-
- import java.security.SecureRandom;
- @Data
- @Component
- public class GlobalConfig {
- @Value("8080")
- private String port;
- @Value("/")
- private String path;
- // @Autowired
- // private DataConfig dataConfig; // 类型注入
- @Qualifier("config")
- private DataConfig dataConfig; // 名字注入
- }
- package com.circle.ioc;
-
- import lombok.Data;
- import org.springframework.beans.factory.annotation.Value;
- import org.springframework.stereotype.Component;
-
- @Data
- @Component(value = "config")
- public class DataConfig {
- @Value("localhost:3306")
- private String url;
- @Value("Drive")
- private String driverName;
- @Value("root")
- private String username;
- @Value("root")
- private String password;
- }
- package com.circle.aop;
-
- public interface cal {
- public int add(int num1, int num2);
- public int sub(int num1, int num2);
- public int mul(int num1, int num2);
- public int div(int num1, int num2);
-
- }
- package com.circle.aop;
-
- public class calculate implements cal{
- @Override
- public int add(int num1, int num2) {
- System.out.println("add方法的参数为:" + num1 + "、" + num2);
- int result = num1 + num2;
- System.out.println("add方法的结果为:" + result);
- return result;
- }
-
- @Override
- public int sub(int num1, int num2) {
- System.out.println("sub方法的参数为:" + num1 + "、" + num2);
- int result = num1 - num2;
- System.out.println("sub方法的结果为:" + result);
- return result;
- }
-
- @Override
- public int mul(int num1, int num2) {
- System.out.println("mul方法的参数为:" + num1 + "、" + num2);
- int result = num1 * num2;
- System.out.println("mul方法的结果为:" + result);
- return result;
- }
-
- @Override
- public int div(int num1, int num2) {
- System.out.println("div方法的参数为:" + num1 + "、" + num2);
- int result = num1 / num2;
- System.out.println("div方法的结果为:" + result);
- return result;
- }
- }
把参数和结果部分的日志代码抽离出业务代码,统一处理,使核心业务代码与非业务代码解耦合。
AOP的优点:
- 可以降低模块之间的耦合性
- 提供代码的复用性
- 提高代码的维护性
- 集中管理非业务代码,便于维护
- 业务代码不受非业务代码影响,逻辑更加清晰
- <dependency>
- <groupId>org.springframework</groupId>
- <artifactId>spring-aop</artifactId>
- <version>5.3.15</version>
- </dependency>
-
- <dependency>
- <groupId>org.aspectj</groupId>
- <artifactId>aspectjweaver</artifactId>
- <version>1.9.7</version>
- </dependency>
- package com.circle.aop;
-
- import org.aspectj.lang.JoinPoint;
- import org.aspectj.lang.annotation.AfterReturning;
- import org.aspectj.lang.annotation.Aspect;
- import org.aspectj.lang.annotation.Before;
- import org.springframework.stereotype.Component;
-
- import java.util.Arrays;
-
- // 切面类
- @Component
- @Aspect
- public class LoggerAspect {
- @Before("execution(public int com.circle.aop.calculate.*(..))")
- public void before(JoinPoint joinPoint){ // 切面对象与方法之间有个连接点对象Joinpoint,需要被横切的位置,即通知要插入业务代码的具体位置
- String name = joinPoint.getSignature().getName();
- System.out.println(name + "方法的参数是:" + Arrays.toString(joinPoint.getArgs()));
- }
-
- @AfterReturning(value = "execution(public int com.circle.aop.calculate.*(..))", returning = "result")
- public void afterReturning(JoinPoint joinPoint, Object result){
- String name = joinPoint.getSignature().getName();
- System.out.println(name + "方法的结果是:" + result);
- }
- }
- @Before,表示方法的执行时机是在业务方法之前,execution表达式表示切入点是calculate中的所有方法
- @AfterReturning,表示方法的执行时机是在业务方法返回结果后,execution表达式表示切入点是calculate类中的方法,returning是把业务方法的返回值和切面类方法的形参进行绑定
- @AfterThrowing,表示方法的执行时机是在业务方法抛出异常后,execution表达式表示切入点是calculate类中的方法,throwing是把业务方法的异常和切面类方法的形参进行绑定
- @After,表示方法的执行时机是在业务方法结束以后,execution表达式表示切入点是calculate类中的方法
- package com.circle.aop;
-
- import org.springframework.stereotype.Component;
- // 目标类
- @Component
- public class calculate implements cal{
- @Override
- public int add(int num1, int num2) {
- int result = num1 + num2;
- return result;
- }
-
- @Override
- public int sub(int num1, int num2) {
- int result = num1 - num2;
- return result;
- }
-
- @Override
- public int mul(int num1, int num2) {
- int result = num1 * num2;
- return result;
- }
-
- @Override
- public int div(int num1, int num2) {
- int result = num1 / num2;
- return result;
- }
- }
在spring.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"
- xmlns:aop="http://www.springframework.org/schema/aop"
- xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd">
-
- <!-- 自动扫包-->
- <context:component-scan base-package="com.circle.aop"></context:component-scan>
-
- <!-- 开启自动生成代理对象-->
- <aop:aspectj-autoproxy></aop:aspectj-autoproxy>
- </beans>
- package com.circle.aop;
-
- import org.springframework.context.ApplicationContext;
- import org.springframework.context.support.ClassPathXmlApplicationContext;
-
- public class Test {
- public static void main(String[] args) {
- ApplicationContext context = new ClassPathXmlApplicationContext("spring.xml");
- cal bean = context.getBean(cal.class); // 代理对象和目标类对象都是实现了接口的类
- bean.add(9, 8);
- bean.sub(9, 8);
- bean.mul(9, 8);
- bean.div(9, 8);
- }
- }
b站楠哥教你学java-1、什么是IoC和AOP_哔哩哔哩_bilibili
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。