Spring架构体系图
Spring(第一版) Spring两大核心机制
IoC IoC是Spring框架的灵魂,控制反转,
1 Student student = new Student ();
程序中的对象不用我们自己手动创建了。
那我们使用Spring的步骤:
开发步骤 1、首先创建maven工程然后导入org.springframework的依赖
lombook可以帮助开发者自动生成实体类相关的方法。在IDEA中使用必须预先安装插件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 <?xml version="1.0" encoding="UTF-8" ?> <project xmlns ="http://maven.apache.org/POM/4.0.0" xmlns:xsi ="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation ="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" > <modelVersion > 4.0.0</modelVersion > <groupId > com.bitzh</groupId > <artifactId > spring</artifactId > <version > 1.0-SNAPSHOT</version > <properties > <maven.compiler.source > 17</maven.compiler.source > <maven.compiler.target > 17</maven.compiler.target > <project.build.sourceEncoding > UTF-8</project.build.sourceEncoding > </properties > <dependencies > <dependency > <groupId > org.springframework</groupId > <artifactId > spring-context</artifactId > <version > 6.1.11</version > </dependency > <dependency > <groupId > org.projectlombok</groupId > <artifactId > lombok</artifactId > <version > 1.18.30</version > </dependency > </dependencies > </project >
2、在resources路径下创建spring.xml.
1 2 3 4 5 6 7 <?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 ="student" class ="com.bitzh.entity.Student" > </bean > </beans >
3、IoC容器通过读取spring.xml配置文件,加载bean标签来创建对象
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 package com.bitzh.test;import com.bitzh.entity.Student;import org.springframework.context.ApplicationContext;import org.springframework.context.support.ClassPathXmlApplicationContext;import java.sql.SQLOutput;public class Test { public static void main (String[] args) { ApplicationContext applicationContext = new ClassPathXmlApplicationContext ("spring.xml" ); Student student = (Student) applicationContext.getBean("student" ); System.out.println(student); } }
4、调用API获取IoC容器中已经创建的对象
IoC容器创建bean的两种方式
1 <bean id ="student" class ="com.bitzh.entity.Student" > </bean >
给成员变量赋值
1 2 3 4 5 <bean id ="student" class ="com.bitzh.entity.Student" > <property name ="id" value ="1" > </property > <property name ="name" value ="张三" > </property > <property name ="age" value ="23" > </property > </bean >
1 2 3 4 5 <bean id ="student2" class ="com.bitzh.entity.Student" > <constructor-arg name ="id" value ="3" > </constructor-arg > <constructor-arg name ="name" value ="张三" > </constructor-arg > <constructor-arg name ="age" value ="18" > </constructor-arg > </bean >
如果不写name就会按照顺序填入,或者用index来配置从0开始
从IoC容器中取bean
1 Student student = (Student) applicationContext.getBean("student" );
通过类型取:当IoC容器中同时存在两个以上Student的bean的时候就会抛异常,因为此时没有唯一的bean
1 Student student = (Student) applicationContext.getBean(Student.class);
bean的属性中如果包含特殊字符,如下处理即可
IoC DI DI指bean之间的依赖注入。依赖注入就是一个对象里面包含另一个对象,设置对象之间的级联关系,比如一个班级里面有学生。
学生类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 package com.bitzh.entity;import lombok.AllArgsConstructor;import lombok.Data;import lombok.NoArgsConstructor;@Data @AllArgsConstructor @NoArgsConstructor public class Student { private Integer id; private String name; private Integer age; private Classes classes; }
班级类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 package com.bitzh.entity;import lombok.Data;@Data public class Classes { private Integer id; private String name; }
依赖注入,把班级注入到学生里面,从学生的角度看是一对一,一个学生对应一个班级
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 <?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 ="classes" class ="com.bitzh.entity.Classes" > <property name ="id" value ="1" > </property > <property name ="name" value ="一班" > </property > </bean > <bean id ="student" class ="com.bitzh.entity.Student" > <property name ="id" value ="100" > </property > <property name ="name" value ="张三" > </property > <property name ="age" value ="22" > </property > <property name ="classes" ref ="classes" > </property > </bean > </beans >
bean之间的级联需要使用ref属性来完成映射,而不能使用value,否则会报错类型转换异常。
如果要将班级里面要把学生注入,从班级的角度来看一个班级多个学生,一对多的关系。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 <?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 ="classes" class ="com.bitzh.entity.Classes" > <property name ="id" value ="1" > </property > <property name ="name" value ="一班" > </property > <property name ="studentList" > <list > <ref bean ="student" > </ref > <ref bean ="student2" > </ref > </list > </property > </bean > <bean id ="student" class ="com.bitzh.entity.Student" > <property name ="id" value ="100" > </property > <property name ="name" value ="张三" > </property > <property name ="age" value ="22" > </property > </bean > <bean id ="student2" class ="com.bitzh.entity.Student" > <property name ="id" value ="101" > </property > <property name ="name" value ="李四" > </property > <property name ="age" value ="18" > </property > </bean > </beans >
注意不能两边同时注入,逻辑不通,班级里面放学生,学生里面放班级,逻辑不通。
Spring中的bean bean是根据scope来完成,表示bean的作用域,scope有4中类型。
singleton ,单例,表示通过Spring容器获取的对象是唯一的、默认值。
prototype,原型,表示通过Spring容器获取的对象是不同的,在属性值里面添加scope然后选择prototype就行了。
request\session适用于web项目。
singleton模式下只要加载IoC容器,无论是否从容器中取出bean,配置文件中的bean都会被创建。
prototype模式下,如果不从IoC容器中取出就不创建,从容器中取出多少次就创建多少次。
Spring的继承 Spring的继承不同于java中的继承,区别:java中的继承是针对于类的,Spring中的继承是针对于对象(bean)。
Spring的继承中,子bean可以继承父bean中的所有成员变量的值。java是针对结构的,Spring是针对值的。用parent属性来继承父bean。
同时子bean可以覆盖父bean的属性值。
两个不同的类,两个bean能完成继承吗?也是可以的。
Spring的继承是针对对象的,所以子bean和父bean并不需要属于同一个数据类型,只要成员列表一致即可。
Spring的依赖 用来设置两个bean的创建顺序。
IoC容器默认情况下是通过spring.xml中bean的配置顺序来决定创建顺序的,配置在前面的bean会先创建。
在不更改sping.xml配置顺序的前提下,通过设置bean之间的依赖关系来调整bean之间的创建顺序。
这样就先创建User,在创建Account.
Spring 读取外部资源 实际开发中,数据库的配置一般会单独保存到后缀为properties中,方便维护和修改,如果使用Spring来加载数据原,就需要在spring.xml中读取properties中的数据,这就是读取外部资源。
jdbc.properties
spring.xml
Spring p 命名空间 p命名空间可以用来简化bean配置
1 2 3 4 5 6 7 8 <?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 ="student" class ="com.bitzh.entity.Student" p:id ="1" p:name ="张三" p:age ="18" p:classes-ref ="class" > </bean > <bean id ="class" class ="com.bitzh.entity.Classes" p:id ="1" p:name ="yiban" > </bean > </beans >
Spring工厂方法 IoC通过工厂模式创建bean有两种模式:
静态工厂方法:
实例工厂方法:
区别在于:静态工厂类不需要实例化,实例工厂类需要实例化
静态工厂方法:
1、创建类
1 2 3 4 5 @Data public class Car { private Integer num; private String brand; }
2、创建静态工厂类,静态工厂方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 package com.bitzh.factory;import com.bitzh.entity.Car;import java.util.HashMap;import java.util.Map;public class StaticCarFactory { private static Map<Integer, Car> carMap; static { carMap = new HashMap <>(); carMap.put(1 ,new Car (1 ,"奥迪" )); carMap.put(2 ,new Car (2 ,"奥拖" )); } public static Car getCar (Integer num) { return carMap.get(num); } }
3、spring.xml
1 2 3 4 5 6 7 8 9 <?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 ="car1" class ="com.bitzh.factory.StaticCarFactory" factory-method ="getCar" > <constructor-arg value ="1" > </constructor-arg > </bean > </beans >
factory-method 指向静态方法
constructor-arg 的 value属性是代用静态方法传入的参数
实力工厂方法
1、创建实例工厂类,工厂方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 package com.bitzh.factory;import com.bitzh.Main;import com.bitzh.entity.Car;import java.util.HashMap;import java.util.Map;public class InstanceCarFactory { private Map<Integer, Car> carMap; public InstanceCarFactory () { carMap = new HashMap <>(); carMap.put(1 ,new Car (1 ,"奥迪" )); carMap.put(2 ,new Car (2 ,"奥拖" )); } public Car getCar (Integer num) { return carMap.get(num); } }
2、spring.xml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 <?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 ="car1" class ="com.bitzh.factory.StaticCarFactory" factory-method ="getCar" > <constructor-arg value ="1" > </constructor-arg > </bean > <bean id ="instatnceCarFactory" class ="com.bitzh.factory.InstanceCarFactory" > </bean > <bean id ="car2" factory-bean ="instatnceCarFactory" factory-method ="getCar" > <constructor-arg value ="2" > </constructor-arg > </bean > </beans >
区别:
静态工厂方法创建Car对象,不需要实例化工厂对象,因为静态工厂的静态方法,不需要创建对象即可调用。spring.xml中只需要配置一个bean,即最终的结果Car即可。
实力工厂方法创建Car对象,需要实例化工厂对象,因为getCar方法是非静态的,就必须通过实例化对象才能调用,所以必须要创建工厂对象,spring.xml中需要配置两个bean,一个是工厂bean,一个是Car bean.
class + factory-method的形式是直接调用类中的工厂方法
spring.xml中factory-bean+factory-method的形式则是调用工厂bean中的工厂方法,就必须先创建工厂bean。
Spring IoC自动装载autowire 自动装载是Spring提供的一种更加简便的方式来完成DI,不需要手动配置peoperty,IoC会自动选择bean来注入。
自动装载有两种方式:
byName,通过属性名完成自动装载
byType,通过属性对应的数据类型来完成自动装载
1、创建Person实体类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 package com.bitzh.entity;import lombok.Data;@Data public class Person { private Integer id; private String name; private Car car; }
2、在spring.xml中配置Carhe Person对应的bean、并且通过自动装载完成依赖注入。
byName操作如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 <?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 ="person" class ="com.bitzh.entity.Person" autowire ="byName" > <property name ="id" value ="1" > </property > <property name ="name" value ="张三" > </property > </bean > <bean id ="car" class ="com.bitzh.entity.Car" > <property name ="num" value ="1" > </property > <property name ="brand" value ="奥迪" > </property > </bean > </beans >
byType操作如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 <?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 ="person" class ="com.bitzh.entity.Person" autowire ="byType" > <property name ="id" value ="1" > </property > <property name ="name" value ="张三" > </property > </bean > <bean id ="car2" class ="com.bitzh.entity.Car" > <property name ="num" value ="1" > </property > <property name ="brand" value ="奥迪" > </property > </bean > </beans >
使用byType自动装载时,必须保证IoC中只有一个符合的bean,否则就会抛出异常。
Spring IoC 基于注解的开发 SpringIoC的作用帮助开发者创建项目中所需要的bean并且完成bean之间的依赖注入关系,DI。
实现该功能有两种方式一种是
基于注解有两步操作,缺一不可:
1、配置自动扫包
2、添加注解
1 <context:component-scan base-package ="com.bitzh.entity" > </context:component-scan >
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 package com.bitzh.entity;import lombok.Data;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.stereotype.Component;@Data @Component public class Repository { private Person person; }
DI
1 2 3 4 5 6 7 8 @Data @Component public class Person { private Integer id; private String name; private Car car; }
1 2 3 4 5 6 @Data @Component(value="myrepo") public class Repository { @Autowired private Person person; }
默认是byType的方式。
@Autowired默认是通过byType注入的,如果要改为byName,需要配置@Qualifier注解来完成
1 2 3 4 5 6 @Data @Component(value = "ds") public class Repository { @Autowired private Person person; }
表示IoC中id为ds的bean注入到respository中。
实体类中普通的成员变量(String,包装类等)可以通过@Value来进行赋值。
1 2 3 4 5 6 7 8 9 @Data @Component(value = "ps") public class Person { @Value("123") private Integer id; @Value("123") private String name; private Car car; }
基于注解怎么配置两个同样的bean呢?利用多态的关系!
写一个接口写两个实现类,这样就等于有了两个实现类,然后注入进去。
但是注入的时候如果把接口注入进去就会发生不是唯一的异常,但是可以把两个实现类注入进去
或者可以用Qualifier来根据名字注入,然后利用多态把接口注入。
实际开发的使用 实际开发中我们会将程序分为三层:
Controller
Service
Repository(DAO)
关系:Contorller调用Service,Service调用Repository
依靠注解的方式完成
1、写Controller层
1 2 3 4 5 6 7 8 9 10 @Setter public class MyController { private MyService myService; public String service (Double score) { return myService.doService(score); } }
2、service层是接口
1 2 3 public interface MyService { public String doService (Double score) ; }
3、对应的实现层
1 2 3 4 5 6 7 8 @Setter public class MyServiceImpl implements MyService { private MyRepository myRepository; @Override public String doService (Double score) { return myRepository.doRepository(score); } }
4、完成Repository层
1 2 3 public interface MyRepository { public String doRepository (Double score) ; }
5、对应的实现层
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 public class MyRepositoryImpl implements MyRepository { @Override public String doRepository (Double score) { String result = "" ; if (score < 60 ){ result = "不及格" ; } if (score >= 60 && score < 80 ){ result = "合格" ; } if (score >= 80 ){ result = "优秀" ; } return result; } }
6、将Repository层注入到service层,将service层注入到controller层。
1 2 3 4 5 6 7 8 <bean id ="controller" class ="com.bitzh.controller.MyController" > <property name ="myService" ref ="service" > </property > </bean > <bean id ="service" class ="com.bitzh.service.impl.MyServiceImpl" > <property name ="myRepository" ref ="repository" > </property > </bean > <bean id ="repository" class ="com.bitzh.repository.impl.MyRepositoryImpl" > </bean >
基于注解的方式
1、完成代码后在实现类中加上@Component并且加上@Autowired,并且加上扫包的xml
1 <context:component-scan base-package ="com.bitzh" > </context:component-scan >
另外,@Component注解是将标注的类加载到IoC容器中,实际开发中,可以根据业务需求,分别使用@Controller,@Service,@Repository注解来注解控制层类,业务层类,持久层类。
Spring IoC的底层原理 核心技术点:xml解析 + 反射
具体的思路:
1、根据需求编写 xml文件,配置需要创建的bean。
2、编写程序读取xml文件,获取bean的相关信息,类,属性,id。
3、根据第二步获取的信息,结合反射机制动态创建对象,同时完成属性的赋值。
4、将创建好的bean存入Map集合,设置key-value映射,key就是bean中的id值,value,就是bean 对象
5、提供方法从Map中通过id获取对应的value。
(利用dom4j解析xml)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 public class MyClassPathXmlApplicationContext implements ApplicationContext { private Map<String ,Object> iocMap; public MyClassPathXmlApplicationContext (String path) { iocMap = new HashMap <>(); parseXML(path); } public void parseXML (String path) { SAXReader saxReader = new SAXReader (); try { Document document = saxReader.read("src/main/resources/" +path); Element root = document.getRootElement(); Iterator<Element> rootIter = root.elementIterator(); while (rootIter.hasNext()){ Element bean = rootIter.next(); String idStr = bean.attributeValue("id" ); String className = bean.attributeValue("class" ); Class clazz = Class.forName(className); Constructor constructor = clazz.getConstructor(); Object object = constructor.newInstance(); Iterator<Element> beanIter = bean.elementIterator(); while (beanIter.hasNext()){ Element property = beanIter.next(); String propertyName = property.attributeValue("name" ); String propertyValue = property.attributeValue("value" ); String methodName = "set" +propertyName.substring(0 ,1 ).toUpperCase()+propertyName.substring(1 ); Field field = clazz.getDeclaredField(propertyName); Method method = clazz.getMethod(methodName,field.getType()); Object value = propertyValue; switch (field.getType().getName()){ case "java.lang.Integer" : value = Integer.parseInt(propertyValue); break ; } method.invoke(object,value); } iocMap.put(idStr,object); } } catch (DocumentException e) { e.printStackTrace(); }catch (ClassNotFoundException e){ e.printStackTrace(); }catch (NoSuchMethodException e){ e.printStackTrace(); }catch (InstantiationException e){ e.printStackTrace(); } catch (InvocationTargetException e) { throw new RuntimeException (e); } catch (IllegalAccessException e) { throw new RuntimeException (e); } catch (NoSuchFieldException e) { throw new RuntimeException (e); } } }
main方法
1 2 3 4 5 6 7 8 public class Test { public static void main (String[] args) { ApplicationContext applicationContext = new MyClassPathXmlApplicationContext ("spring-Ioc.xml" ); Car car = (Car) applicationContext.getBean("car" ); System.out.println(car); } }
xml
1 2 3 4 5 6 7 8 9 10 <?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.bitzh.entity.Car" > <property name ="num" value ="1" > </property > <property name ="brand" value ="奥迪" > </property > </bean > </beans >
JDK动态代理 代理模式 是通过代理对象访问目标对象,这样可以在目标对象基础上增强额外的功能,如添加权限,访问控制和审计等功能。 房产中介代替业主卖房
代理模式的目的:在不修改原有代码的 或者没有办法修改原有代码的情况下 增强对象功能 使用代理对象 代替原来的对象去完成功能 进而达到拓展功能的目的
静态代理 静态代理中代理类与被代理类都需要实现同一个接口,这就说明我们的一个静态代理类只能代理一个类,并且还要事先知道我们要代理哪个类才能写代理类,如果我们有其他类还想使用代理那就必须再写一个代理类。然而在实际开发中我们是可能是有非常多的类是需要被代理的,并且事先我们可能并不知道我们要代理哪个类。所以如果继续使用静态代理反而会增加许多的工作量,并且效率低下,代码复用率也不好。
下面用张三打官司举例子
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 package com.bitzh.test;public class Test { public static void main (String[] args) { Person person = new Person ("张三" ); Court court = new Lawyer (person); court.doCourt(); } } interface Court { public void doCourt () ; } class Lawyer implements Court { private Person person; public Lawyer (Person person) { this .person = person; } @Override public void doCourt () { System.out.println("律师取证:视频证明张三不在案发现场" ); System.out.println("律师总结张三不可能犯罪" ); person.doCourt(); } } class Person implements Court { private String name; public Person (String name) { this .name = name; } @Override public void doCourt () { System.out.println(name+"说:我没有犯罪" ); } }
动态代理 动态代理可以针对于一些不特定的类或者一些不特定的方法进行代理,我们可以在程序运行时动态的变化代理的规则,代理类在程序运行时才创建的代理模式成为动态代理。这种情况下,代理类并不是在Java代码中定义好的,而是在程序运行时根据我们的在Java代码中的“指示”动态生成的 Proxy 动态代理 JDK动态代理 面向接口 cglib 动态代理 第三方动态代理 面向父类
下面用张三吃饭的例子
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 package com.bitzh.test;import java.lang.reflect.InvocationHandler;import java.lang.reflect.Method;import java.lang.reflect.Proxy;public class Test2 { public static void main (String[] args) { Dinner dinner = new Person1 ("张三" ); ClassLoader classLoader = dinner.getClass().getClassLoader(); Class[] interfaces = dinner.getClass().getInterfaces(); InvocationHandler myInvocationHandler = new InvocationHandler (){ Object res = null ; @Override public Object invoke (Object proxy, Method method, Object[] args) throws Throwable { System.out.println("invoker方法执行了" ); if (method.getName().equals("eat" )){ System.out.println("饭前洗手" ); res = method.invoke(dinner, args); System.out.println("饭后刷碗" ); }else { res = method.invoke(dinner, args); } return res; } }; Dinner dinnerProxy = (Dinner) Proxy.newProxyInstance(classLoader,interfaces , myInvocationHandler); dinnerProxy.eat("包子" ); } } interface Dinner { public void eat (String foodName) ; } class Person1 implements Dinner { private String name; public Person1 (String name) { this .name = name; } @Override public void eat (String foodName) { System.out.println(name+"正在吃" +foodName); } } class Student implements Dinner { private String name; public Student (String name) { this .name = name; } @Override public void eat (String foodName) { System.out.println(name+"正在吃" +foodName); } }
Spring AOP AOP (Aspect Oriented Programming)面向切面编程。
OOP(Object Oriented Programming)面向对象编程,用对象化的思想来完成程序。
AOP是对OOP的一个补充,是在另外一个维度上抽象出对象。
具体是指程序运行时动态的将非业务代码切入到业务代码中,从而实现程序的解耦合,将非业务代码抽象成一个对象,对对象编程就是面向切面编程。
这种代码复用性不高,维护性差。
使用AOP优化。
AOP的优点:
可以降低模块之间的耦合性
提高代码的复用性
提高代码的维护性
集中管理非业务代码,便于维护
业务代码不受非业务代码的影响,逻辑更加清晰
通过一个例子来理解AOP。
1、创建一个计算器的接口
1 2 3 4 5 6 7 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) ; }
2、创建接口的实现类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 public class CalImpl 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; } }
日志打印
在每个方法开始位置输出参数信息。
在每个方法结束位置输出结果信息。
对于计算器来讲、加减乘除就是业务代码,日志打印就是非业务代码。
AOP如何实现?使用动态代理方式来实现。
代理首先应该具备CalImpl的所有功能,并再次基础上,扩展出打印日志的功能。
1、删除CalImple方法中的所有打印日志代码,只保留业务代码。
2、创建MyInvocationHandler类,实现InvocationHandler接口,生成动态代理类。
动态代理类,需要动态生成,需要获取到委托类的接口信息,根据这些接口信息动态生成一个代理类,然后再ClassLoader用来将动态生成的类加载到JVM中
就是说:代理类不是手动创建的,所以需要通过ClassLoader加载到内存中,同时,代理类具有被代理类的功能(接口实现)。
类加载器是统一的,每个类都是通过系统的类加载器加载到虚拟机中的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 public class MyInvocationHandler implements InvocationHandler { private Object object = null ; public Object bind (Object object) { this .object = object; 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(method.getName()+" " +Arrays.toString(args)); Object result = method.invoke(this .object, args); System.out.println(method.getName()+" " +result); return result; } }
1 2 3 4 5 6 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) ; }
1 2 3 4 5 6 7 8 9 10 11 public class Test { public static void main (String[] args) { Cal cal = new CalImpl (); MyInvocationHandler myInvocationHandler = new MyInvocationHandler (); Cal proxy = (Cal) myInvocationHandler.bind(cal); proxy.add(10 ,3 ); } }
上述代码通过动态代理机制实现了业务代码和非业务代码的解耦合,这是SpringAOP的底层实现机制,真正在使用SpringAOP进行开发时,不需要这么复杂,可以更好的理解的方式来开发。
1、创建切面类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 package com.bitzh.aop;import org.aspectj.lang.JoinPoint;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.bitzh.aop.impl.CalImpl.add(..))") public void before (JoinPoint joinPoint) { String name = joinPoint.getSignature().getName(); String args = Arrays.toString(joinPoint.getArgs()); System.out.println(name+" " +args); } }
2、委托类也需要加入@Component
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 package com.bitzh.aop.impl;import com.bitzh.aop.Cal;import org.springframework.stereotype.Component;@Component public class CalImpl 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; } }
3、spring.xml
1 2 3 4 5 <context:component-scan base-package ="com.bitzh" > </context:component-scan > <aop:aspectj-autoproxy > </aop:aspectj-autoproxy >
@Component,将切面类加载到IoC容器中
@Aspect,表示该类是一个切面类
@Before,表示方法的实行实际是在业务方法之前。execution表达式表示切入点是CalImpl类中的add方法
aop:aspectj-autoproxy,Spring IoC容器会结合切面对象和委托对象自动生成动态代理对象,AOP底层就是通过动态代理机制来实现的。
@After,表示方法的实行实际是在业务方法之后。execution表达式表示切入点是CalImpl类中的add方法
@AfterReturning,表示方法的执行时机是在业务方法返回结果之后,returning是将业务方法的返回值与切面类方法的形参进行绑定
@AfterThrowing,表示方法的执行时机是在业务方法抛出异常后execution表达式表示切入点,throwing是将业务方法的返回值与切面类方法的形参进行绑定
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 @Component @Aspect public class LoggerAspect { @Before("execution(public int com.bitzh.aop.impl.CalImpl.*(..))") public void before (JoinPoint joinPoint) { String name = joinPoint.getSignature().getName(); String args = Arrays.toString(joinPoint.getArgs()); System.out.println(name+" " +args); } @After("execution(public int com.bitzh.aop.impl.CalImpl.*(..))") public void after (JoinPoint joinPoint) { String name = joinPoint.getSignature().getName(); System.out.println(name+"执行完毕" ); } @AfterReturning(value = "execution(public int com.bitzh.aop.impl.CalImpl.*(..))",returning = "result") public void afterReturn (JoinPoint joinPoint,Object result) { String name = joinPoint.getSignature().getName(); System.out.println(name + result); } @AfterThrowing(value = "execution(public int com.bitzh.aop.impl.CalImpl.*(..))",throwing = "ex") public void afterThrowing (JoinPoint joinPoint,Exception ex) { String name = joinPoint.getSignature().getName(); System.out.println(name + ex); } }
AOP的概念:
切面对象:根据切面抽象出来的一个对象,CalImpl 所有方法中需要加入日志的部分,抽象成一个切面类LoggerAspect
通知:切面对象具体执行的代码,非业务代码,LoggerAspect对象打印日志的代码。
目标:被切面对象,即CalImpl,将通知加入其中。
代理:切面对象、通知、目标混合之后的结果,即我们使用JDK动态代理机制创建的对象
连接点:需要被横切的位置,即通知要插入业务代码的具体位置。
Spring(第二版和第一版有重复)
Spring_IOC(控制反转) 简单的说就是,创建对象的权利,或者是控制的位置,由JAVA代码转移到spring容器,由spring的容器控制对象的创建,就是控制反转,spring创建对象时,会读取配置文件中的信息,然后使用反射给我们创建好对象之后在容器中存储起来,当我们需要某个对象时,通过id获取对象即可,不需要我们自己去new.一句话:创建对象交给容器
传统的是这样的
现在是这样
IOC代码测试 新建项目然后导入依赖
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 <dependencies > <dependency > <groupId > org.springframework</groupId > <artifactId > spring-core</artifactId > <version > 5.3.5</version > </dependency > <dependency > <groupId > org.springframework</groupId > <artifactId > spring-beans</artifactId > <version > 5.3.5</version > </dependency > <dependency > <groupId > org.springframework</groupId > <artifactId > spring-context</artifactId > <version > 5.3.5</version > </dependency > <dependency > <groupId > org.springframework</groupId > <artifactId > spring-expression</artifactId > <version > 5.3.5</version > </dependency > </dependencies >
对应jar包
四个依赖介绍 spring-context 上下文,容器 spring-beans 创建对象 spring-core 核心jar spring-expression 表达式jar
但是事实上,我们导入spring-context的时候,会自动导入其他依赖的jar,自动进行了依赖传递 所以,导入一个spring-context 依赖也可以
为了方便测试,我们导入Junit测试依赖
1 2 3 4 5 6 <dependency > <groupId > junit</groupId > <artifactId > junit</artifactId > <version > 4.13.1</version > <scope > test</scope > </dependency >
测试代码
实现类
接口
这样写当调用不同的对象或者创建对象就只用改变配置文件了,创建对象的权利就转移给容器了。
IOC实现原理
1 XML解析技术读取配置文件
1 <bean id ="empDao" class ="com.msb.dao.impl.EmpDaoImpl" > </bean >
将上面的信息读取进入程序 对象的ID ,一个是对象的类的全路径名
2 反射技术实例化对象,放到容器中 获得类的字节码 Class clazz =Class.forName(“com.msb.dao.impl.EmpDaoImpl”) 通过字节码实例化对象 Object obj = clazz.newInstance(); 将对象放到一个map集合中 map.put(“empDao”,obj)
3 工厂模式返回Bean对象 getBean方法
其实那个name其实就是bean id
public Object getBean(String name){ Object obj =map.get(name); return obj; }
IOC接口 BeanFactory 接口: IOC容器基本功能接口,是spring内部使用的接口,我们在处理业务时一般不直接使用该接口 ApplicationContext 接口: BeanFactory的子接口,提供更多更强大的功能,研发人员一般使用的接口
按住ctrl+h可以看到下面的子接口和类
XML实现方式DI spring中的Bean的管理: Bean(汉译咖啡豆). 又称JAVABean.其实就是JAVA程序程序中的一个个对象,所以Bean的管理其实就是spring对于JAVA程序中的对象的管理
管理的内容是什么
1 对象的创建 IOC IOC 叫做控制反转,就是Spring给我们创建对象,然后我们直接用,不用自己NEW,前面已经解释过 IOC处理的是对象如何创建的问题
2 属性的赋值 DI DI Dependency Injection,即“依赖注入” 就是创建属性时给对象属性赋值 对象功能的实现往往要依赖属性的值,那么给对象属性赋值就可以说成是依赖注入 由于对象属性不仅仅是基本数据类型,还可能是其他类,或者引用类型 那么依赖注入将会把更多的对象之间的关系整理到一起,可以行程一个庞大的依赖关系 DI处理的是对象的属性赋值和互相依赖的关系
spring给我们提供了两种关于bean的方式
1 基于XML方式的Bean管理
2 基于注解方式的Bean管理
依赖
1 2 3 4 5 6 7 8 9 10 11 12 <dependency > <groupId > org.springframework</groupId > <artifactId > spring-context</artifactId > <version > 5.3.5</version > </dependency > <dependency > <groupId > junit</groupId > <artifactId > junit</artifactId > <version > 4.13.1</version > <scope > test</scope > </dependency >
创建spring配置文件,一般spring的配置文件很多人约定俗称为application** .xml
准备一个要实例化的类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 public class User { private Integer userid; private String username; private String password; @Override public String toString () { return "User{" + "userid=" + userid + ", username='" + username + '\'' + ", password='" + password + '\'' + '}' ; } public User () { System.out.println("noArgConstructor" ); } public User (Integer userid, String username, String password) { System.out.println("allArgConstructor" ); this .userid = userid; this .username = username; this .password = password; } public void setUserid (Integer userid) { System.out.println("setUserid" ); this .userid = userid; } public void setUsername (String username) { System.out.println("setUsername" ); this .username = username; } public void setPassword (String password) { System.out.println("setpassword" ); this .password = password; } }
IOC创建对象 通过无参构造方法构造对象
1 <bean id ="user1" class ="com.msb.bean.User" > </bean >
标签的常见属性
1 <bean id ="user1" class ="com.msb.bean.User" name ="user1" scope ="prototype" lazy-init ="true" > </bean >
id 对象的id class 类的全路径名 name 和id类似,一般不用 scope 控制对象单例多例和使用范围 singleton作用域(scope 默认值), Spring IOC容器中只会存在一个共享的bean实例 prototype作用域部署的bean,每一次获取都会产生一个新的bean实例,相当与一个new的操作 request表示该针对每一次HTTP请求都会产生一个新的bean,同时该bean仅在当前HTTP request内有效 session作用域表示该针对每一次HTTP请求都会产生一个新的bean,同时该bean仅在当前HTTP session内有效 global session作用域类似于标准的HTTP Session作用域,不过它仅仅在基于portlet的web应用中才有意义 lazy-init 懒加载 调用getBean的时候再去实例化对象
DI给对象属性赋值1 通过set方法给对象属性赋值
1 2 3 4 5 6 <bean id ="user1" class ="com.msb.bean.User" > <property name ="userid" value ="1" > </property > <property name ="username" value ="张三" > </property > <property name ="password" value ="abcdefg" > </property > </bean >
2 通过有参构造给对象属性赋值
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 <bean id ="user2" class ="com.msb.bean.User" > <constructor-arg name ="userid" value ="2" > </constructor-arg > <constructor-arg name ="username" value ="小明" > </constructor-arg > <constructor-arg name ="password" value ="123456789" > </constructor-arg > </bean > <bean id ="user3" class ="com.msb.bean.User" > <constructor-arg index ="0" value ="3" > </constructor-arg > <constructor-arg index ="1" value ="小黑" > </constructor-arg > <constructor-arg index ="2" value ="987654321" > </constructor-arg > </bean >
3 通过p名称空间和c名称空间给对象属性赋值
添加约束
1 2 3 4 5 6 7 <?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" 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" >
配置对象
1 2 3 4 <bean id ="user4" class ="com.msb.bean.User" p:userid ="4" p:username ="小东" p:password ="111111" > </bean > <bean id ="user5" class ="com.msb.bean.User" c:userid ="5" c:username ="小西" c:password ="222222" > </bean >
4 注入空值和特殊符号
1 2 3 4 5 6 7 8 9 10 11 12 <bean id ="user1" class ="com.msb.bean.User" > <property name ="userid" > <null > </null > </property > <property name ="username" value ="& xiaoming< > " > </property > <property name ="password" > <value > <![CDATA[&<123456>]]></value > </property > </bean >
5关于bean引用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 <?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 ="date1" class ="java.util.Date" > </bean > <bean id ="Mouse1" class ="com.bitzh.bean.Mouse" > <property name ="name" value ="Jerry" > </property > <property name ="birthday" ref ="date1" > </property > </bean > <bean id ="cat1" class ="com.bitzh.bean.Cat" > <property name ="name" value ="Tom" > </property > <property name ="mouse1" > <bean class ="com.bitzh.bean.Mouse" > <property name ="name" value ="Jerry2" > </property > <property name ="birthday" ref ="date1" > </property > </bean > </property > </bean > </beans >
6 关于集合注入
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 <?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" xmlns:c ="http://www.springframework.org/schema/c" 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 " > <util:list id ="outerbookList" > <bean id ="b1" class ="com.bitzh.bean.Book" p:bname ="JAVA" p:author ="oyy" > </bean > <bean id ="b2" class ="com.bitzh.bean.Book" p:bname ="Go" p:author ="oyy" > </bean > <bean id ="b3" class ="com.bitzh.bean.Book" p:bname ="JVM" p:author ="oyy" > </bean > </util:list > <bean id ="student1" class ="com.bitzh.bean.Student" > <property name ="books" > <array > <value > JAVA</value > <value > MySQL</value > <value > Spring</value > </array > </property > <property name ="bookSet" > <set > <value > JAVA</value > <value > MySQL</value > <value > Spring</value > </set > </property > <property name ="bookList" > <list > <value > JAVA</value > <value > MySQL</value > <value > Spring</value > </list > </property > <property name ="bookMap" > <map > <entry key ="JAVA" value ="oyy" > </entry > <entry key ="Go" value ="oyy" > </entry > <entry key ="JVM" value ="oyy" > </entry > </map > </property > <property name ="bookList2" ref ="outerbookList" > </property > </bean > </beans >
7 工厂方式获取bean
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 public class BookFactory implements FactoryBean <Book> { public Book getObject () throws Exception { Book book = new Book (); book.setBname("Java" ); book.setAuthor("oyy" ); return book; } public Class<?> getObjectType() { return null ; } }
1 <bean id ="bookFactory" class ="com.bitzh.bean.BookFactory" >
1 2 3 4 5 6 7 8 public class Test3 { @Test public void testGetBean () { ApplicationContext applicationContext = new ClassPathXmlApplicationContext ("applicationContext3.xml" ); Book bookFactory = applicationContext.getBean("bookFactory" , Book.class); System.out.println(bookFactory); } }
bean生命周期 bean从创建到销毁经历的各个阶段以及每个阶段所调用的方法 1 通过构造器创建bean实例 执行构造器 2 为bean属性赋值 执行set方法 3 初始化bean 调用bean的初始化方法,需要配置指定调用的方法 4 bean的获取 容器对象 getBean方法 5 容器关闭销毁bean 调用销毁方法,需要配置指定调用的方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 public class User { private Integer userid; private String username; private String password; public void initUser () { System.out.println("第三步:User初始化" ); } public User () { System.out.println("第一步:User构造" ); } public void destoryUser () { System.out.println("第五步:User销毁" ); } @Override public String toString () { return "User{" + "userid=" + userid + ", username='" + username + '\'' + ", password='" + password + '\'' + '}' ; } public User (Integer userid, String username, String password) { this .userid = userid; this .username = username; this .password = password; } public void setUserid (Integer userid) { System.out.println("setUserid" ); this .userid = userid; } public void setUsername (String username) { System.out.println("第二步:User属性赋值" ); this .username = username; } public void setPassword (String password) { this .password = password; } }
配置bean
1 2 3 4 5 6 7 8 9 10 11 <?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" 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" > <bean id ="user" class ="com.msb.bean.User" init-method ="initUser" destroy-method ="destoryUser" > <property name ="username" value ="xiaoming" > </property > </bean > </beans >
测试代码
1 2 3 4 5 6 7 8 9 10 11 public class Test1 { @Test public void testGetBean () { ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext ("applicationContext.xml" ); User user = context.getBean("user" ,User.class); System.out.println("第四步:User对象从容器中获取" ); context.close(); } }
关于后置处理器
简而言之就是要是想在bean初始化之前还有点操作就可以创建一个后置处理器,提前操作。
1 通过构造器创建bean实例 执行构造器 2 为bean属性赋值 执行set方法 3 把bean实例传递给bean的后置处理器的方法 4 初始化bean 调用bean的初始化方法,需要配置指定调用的方法 5 把bean实例传递给bean的后置处理器的方法 6 bean的获取 容器对象 getBean方法 7 容器关闭销毁bean 调用销毁方法,需要配置指定调用的方法
BeanPostProcessor接口作用:
如果我们想在Spring容器中完成bean实例化、配置以及其他初始化方法前后要添加一些自己逻辑处理。我们需要定义一个或多个BeanPostProcessor接口实现类,然后注册到Spring IoC容器中。
1、接口中的两个方法都要将传入的bean返回,而不能返回null,如果返回的是null那么我们通过getBean方法将得不到目标。 2、ApplicationContext会自动检测在配置文件中实现了BeanPostProcessor接口的所有bean,并把它们注册为后置处理器,然后在容器创建bean的适当时候调用它,因此部署一个后置处理器同部署其他的bean并没有什么区别。而使用BeanFactory实现的时候,bean 后置处理器必须通过代码显式地去注册,在IoC容器继承体系中的ConfigurableBeanFactory接口中定义了注册方法
1 创建后置处理器 实现 BeanPostProcesser 重写两个方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 public class MyBeanProcesser implements BeanPostProcessor { @Override public Object postProcessBeforeInitialization (Object bean, String beanName) throws BeansException { System.out.println("bean:初始化方法之前" ); return bean; } @Override public Object postProcessAfterInitialization (Object bean, String beanName) throws BeansException { System.out.println("bean:初始化方法之后" ); return bean; } }
2 配置后置处理器,对容器中的所有bean添加后置处理器的生命周期
1 2 3 4 5 6 7 8 9 10 11 12 <?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" 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" > <bean id ="user" class ="com.msb.bean.User" init-method ="initUser" destroy-method ="destoryUser" > <property name ="username" value ="xiaoming" > </property > </bean > <bean id ="myBeanProcesser" class ="com.msb.beanProcesser.MyBeanProcesser" > </bean > </beans >
bean的自动装配 通过property标签可以手动指定给属性进行注入 我们也可以通过自动转配,完成属性的自动注入,就是自动装配,可以简化DI的配置
简单来说就是可以在bean里面完成对于实体类的属性和值的注入
准备实体类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 public class Emp { private Dept dept; @Override public String toString () { return "Emp{" + "dept=" + dept + '}' ; } public Dept getDept () { return dept; } public void setDept (Dept dept) { this .dept = dept; } public Emp () { } public Emp (Dept dept) { this .dept = dept; } }
配置文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 <?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" 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" > <bean id ="dept" class ="com.bitzh.bean.Dept" > </bean > <bean id ="emp" class ="com.bitzh.bean.Emp" autowire ="byName" > </bean > </beans >
测试代码
1 2 3 4 5 6 7 8 public class Test2 { @Test public void testGetBean () { ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext ("applicationContext2.xml" ); Emp emp = context.getBean("emp" , Emp.class); System.out.println(emp); } }
读取属性配置文件 之前我们的值已经写死了,现在可不可以在mybatis中的jdbc.properties中读取
数据库连接池技术
实际开发中我们不需要自己写,直接用别人写好的 比如使用spring中写好的c3p0连接池还有德鲁伊连接池Druid
现在我们使用德鲁伊连接池但是一开始有些基本属性需要我们自己去配置
现在在使用连接池初始化之后我们需要对里面的driver,url,password中进行赋值但是我们对这些信息不会直接写在Context中而是写在property中。
以后得连接池不叫连接池了而是叫数据源 然后再数据原里面配置数据库信息
首先先导入德鲁伊的依赖还有mysql-connecter的依赖
1 2 3 4 5 <dependency > <groupId > com.alibaba</groupId > <artifactId > druid</artifactId > <version > 1.2.21</version > </dependency >
applicationContext中添加context名称空间 并读取属性配置文件 配置druid数据源将属性配置文件中的信息注入到连接池中
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 <?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" xmlns:c ="http://www.springframework.org/schema/c" 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 " > <context:property-placeholder location ="classpath:jdbc.properties" /> <bean id ="dataSource" class ="com.alibaba.druid.pool.DruidDataSource" > <property name ="username" value ="${jdbc_username}" > </property > <property name ="password" value ="${jdbc_password}" > </property > <property name ="url" value ="${jdbc_url}" > </property > <property name ="driverClassName" value ="${jdbc_driver}" > </property > </bean > </beans >
测试代码
1 2 3 4 5 6 7 8 9 10 public class Test4 { @Test public void testGetBean () throws SQLException { ApplicationContext applicationContext = new ClassPathXmlApplicationContext ("applicationContext4.xml" ); DruidDataSource dataSource = applicationContext.getBean("dataSource" , DruidDataSource.class); System.out.println(dataSource); } }
注解方式实现IOC 将来实际研发中用的更多的是注解而不是用xml来写
1注解方式创建对象IOC
导入依赖 aop
@Component 放在类上,用于标记,告诉spring当前类需要由容器实例化bean并放入容器中 该注解有三个子注解 @Controller 用于实例化controller层bean @Service 用于实例化service层bean @Repository 用于实例化持久层bean 当不确定是哪一层,就用Component 这几个注解互相混用其实也可以,但是不推荐
第一步:在applicationContext.xml中配置开启注解扫描
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 <?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" xmlns:c ="http://www.springframework.org/schema/c" 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 " > <context:component-scan base-package ="com.msb.bean" > </context:component-scan > </beans >
第二步:在类上添加注解,让spring容器给我们创建bean实例并存储于容器中
1 2 3 4 5 6 7 8 9 10 11 12 13 14 package com.bitzh.bean;import org.springframework.stereotype.Component;@Component(value = "user") public class User {}
测试代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 package com.bitzh.test;import com.bitzh.bean.User;import org.junit.Test;import org.springframework.context.ApplicationContext;import org.springframework.context.support.ClassPathXmlApplicationContext;public class Test1 { @Test public void testGetBean () { ApplicationContext context = new ClassPathXmlApplicationContext ("applicationContext4.xml" ); User user = context.getBean("user" , User.class); System.out.println(user); } }
组件扫描配置注解识别
2注解方式依赖注入DI
@Autowired 根据属性数据类型自动装配 这个用的最多 @Qualifier 根据属性名称注入依赖 @Resources 可以根据类型,也可以根据名称注入 @Value 注入普通数据类型(8+String)
其实就是有一个接口,还有一个实现类,当需要用到接口的某个功能时,直接利用注解让接口的属性由实现类去赋值
Dao层 接口
实现类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 @Service public class UserServiceImpl implements UserService { @Autowired private UserDao userDao; @Value("张三") private String name; private Integer sage; @Override public void add () { System.out.println("UserServiceImpl" ); System.out.println(name); userDao.add(); } }
这样的话呢要是不想写死就可以创建一个properties然后把值放在里面,然后配置文件的时候用
1 2 <context:property-placeholder location ="classpath:*.properties" > </context:property-placeholder > <context:component-scan base-package ="com.bitzh" > </context:component-scan >
这样就可以把配置文件中的基本属性赋值直接读取,然后用${}来赋值了
1 2 @Value("${username}") private String sname;
中文乱码问题
配置类方式实现IOC和DI 配置文件现在变成这样了就是之前告诉包扫描文件的xml变成了java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 package com.bitzh.config;import org.springframework.context.annotation.ComponentScan;@ComponentScan(basePackages = "com.bitzh") @PropertySource("classpath:*.properties") public class SpringConfig {}
测试代码就要变成扫描SpringConfig的字节码文件了
1 2 3 4 5 6 @Test public void testGetBean2 () { ApplicationContext context = new AnnotationConfigApplicationContext (SpringConfig.class); UserServiceImpl userService = context. getBean("userServiceImpl" ,UserServiceImpl.class); userService.add(); }
代理模式概念和静态代理 代理模式 是通过代理对象访问目标对象,这样可以在目标对象基础上增强额外的功能,如添加权限,访问控制和审计等功能。 房产中介代替业主卖房
静态代理 静态代理中代理类与被代理类都需要实现同一个接口,这就说明我们的一个静态代理类只能代理一个类,并且还要事先知道我们要代理哪个类才能写代理类,如果我们有其他类还想使用代理那就必须再写一个代理类。然而在实际开发中我们是可能是有非常多的类是需要被代理的,并且事先我们可能并不知道我们要代理哪个类。所以如果继续使用静态代理反而会增加许多的工作量,并且效率低下,代码复用率也不好。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 package com.bitzh.test;public class Test01 { public static void main (String[] args) { Person person = new Person ("张三" ); Court court = new Lawyer (person); court.doCourt(); } } interface Court { void doCourt () ; } class Lawyer implements Court { private Person person; public Lawyer (Person person) { this .person = person; } @Override public void doCourt () { System.out.println("律师取证" ); person.doCourt(); } } class Person implements Court { private String name; public Person (String name) { this .name = name; } @Override public void doCourt () { System.out.println(name+"说:我没杀人" ); } }
动态代理 动态代理可以针对于一些不特定的类或者一些不特定的方法进行代理,我们可以在程序运行时动态的变化代理的规则,代理类在程序运行时才创建的代理模式成为动态代理。这种情况下,代理类并不是在Java代码中定义好的,而是在程序运行时根据我们的在Java代码中的“指示”动态生成的 Proxy 动态代理 JDK动态代理 面向接口 cglib 动态代理 第三方动态代理 面向父类
张三吃饭的例子
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 package com.bitzh.test.testProxy;import java.lang.reflect.InvocationHandler;import java.lang.reflect.Method;import java.lang.reflect.Proxy;import java.util.Arrays;public class Test1 { public static void main (String[] args) { final Dinner dinner=new Person ("张三" ); ClassLoader classLoader = dinner.getClass().getClassLoader(); Class[] interaces= dinner.getClass().getInterfaces(); InvocationHandler handler = new InvocationHandler (){ public Object invoke (Object proxy, Method method, Object[] args) throws Throwable { Object res=null ; if (method.getName().equals("eat" )){ System.out.println("饭前洗手" ); res =method.invoke(dinner, args); System.out.println("饭后刷碗" ); }else { res =method.invoke(dinner, args); } return res; } }; Dinner dinnerProxy = (Dinner) Proxy.newProxyInstance(classLoader,interaces,handler); dinnerProxy.eat("包子" ); } } interface Dinner { void eat (String foodName) ; void drink () ; } class Person implements Dinner { private String name; public Person (String name) { this .name = name; } @Override public void eat (String foodName) { System.out.println(name+"正在吃" +foodName); } @Override public void drink ( ) { System.out.println(name+"正在喝茶" ); } } class Student implements Dinner { private String name; public Student (String name) { this .name = name; } @Override public void eat (String foodName) { System.out.println(name+"正在食堂吃" +foodName); } @Override public void drink ( ) { System.out.println(name+"正在喝可乐" ); } }
使用代理技术 获得代理对象 代替张三 增强打官司的方法
总结
1 在不修改原有代码的 或者没有办法修改原有代码的情况下 增强对象功能 使用代理对象 代替原来的对象去完成功能 进而达到拓展功能的目的 2 JDK Proxy 动态代理面向接口的动态代理 一定要有接口和实现类的存在 代理对象增强的是实现类 在实现接口的方法重写的方法 生成的代理对象只能转换成 接口的不能转换成 被代理类 代理对象只能增强接口中定义的方法 实现类中其他和接口无关的方法是无法增强的 代理对象只能读取到接口中方法上的注解 不能读取到实现类方法上的注解
Spring_CGLIB动态代理 proxy 动态代理 面向接口 1必须有接口和实现类 2增强接口中定义的方法 3只能读取接口中方法的上注解
cglib动态代理模式 面向父类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 public class Test1 { @Test public void testCglib () { Person person = new Person (); Enhancer enhancer=new Enhancer (); enhancer.setSuperclass(person.getClass()); MethodInterceptor methodInterceptor=new MethodInterceptor () { @Override public Object intercept (Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable { Object res = null ; if (method.getName().equals("eat" )){ System.out.println("饭前洗手" ); res=methodProxy.invokeSuper(o,objects); System.out.println("饭后刷碗" ); }else { res=methodProxy.invokeSuper(o,objects); } return res; } }; enhancer.setCallback(methodInterceptor); Person personProxy = (Person)enhancer.create(); personProxy.eat("包子" ); } } class Person { public Person ( ) { } public void eat (String foodName) { System.out.println("张三正在吃" +foodName); } }
AOP面向切面的编程 AOP切面编程一般可以帮助我们在不修改现有代码的情况下,对程序的功能进行拓展,往往用于实现 日志处理,权限控制,性能检测,事务控制等 AOP实现的原理就是动态代理,在有接口的情况下,使用JDK动态代理,在没有接口的情况下使用cglib动态代理
为Dao层所有的add方法添加一个性能记录功能
1 连接点 Joint point: 类里面那些可以被增强的方法 ,这些方法称之为连接点 表示在程序中明确定义的点,典型的包括方法调用,对类成员的访问以及异常处理程序块的执行等等,它自身还可以嵌套其它 joint point
2 切入点 Pointcut:实际被增强的方法 ,称之为切入点 表示一组 joint point,这些 joint point 或是通过逻辑关系组合起来,或是通过通配、正则表达式等方式集中起来,它定义了相应的 Advice 将要发生的地方
3 通知 Advice:实际增强的逻辑部分 称为通知 (增加的功能) Advice 定义了在 Pointcut 里面定义的程序点具体要做的操作,它通过 before、after 和 around 来区别是在每个 joint point 之前、之后还是代替执行的代码。 通知类型:
1 前置通知 (放在前面的通知)
2 后置通知 (无论异常都执行)
3 环绕通知 (前后都有)
4 异常通知 (出了异常才执行)
5 最终通知 (不出异常才通知)
4 目标对象 Target:被增强功能的对象(被代理的对象) 织入 Advice 的目标对象
5 切面Aspect: 表现为功能相关的一些advice方法放在一起声明成的一个Java类 Aspect 声明类似于 Java 中的类声明,在 Aspect 中会包含着一些 Pointcut 以及相应的 Advice。
6 织入 Weaving:创建代理对象并实现功能增强的声明并运行过程 将 Aspect 和其他对象连接起来, 并创建 Adviced object 的过程
AOP注解方式实现——掌握 实现的两种方式 1 基于注解方式实现 (熟练) 2 基于XML配置方式 (了解)
1导入依赖
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 <dependencies > <dependency > <groupId > org.springframework</groupId > <artifactId > spring-context</artifactId > <version > 5.3.5</version > </dependency > <dependency > <groupId > org.springframework</groupId > <artifactId > spring-aspects</artifactId > <version > 5.3.5</version > </dependency > <dependency > <groupId > aopalliance</groupId > <artifactId > aopalliance</artifactId > <version > 1.0</version > </dependency > <dependency > <groupId > commons-logging</groupId > <artifactId > commons-logging</artifactId > <version > 1.2</version > </dependency > <dependency > <groupId > com.alibaba</groupId > <artifactId > druid</artifactId > <version > 1.1.10</version > </dependency > <dependency > <groupId > mysql</groupId > <artifactId > mysql-connector-java</artifactId > <version > 8.0.22</version > </dependency > <dependency > <groupId > junit</groupId > <artifactId > junit</artifactId > <version > 4.13.1</version > <scope > test</scope > </dependency > <dependency > <groupId > org.projectlombok</groupId > <artifactId > lombok</artifactId > <version > 1.18.12</version > <scope > provided</scope > </dependency > </dependencies >
开启注解扫描和AOP切面编程自动生成代理对象配置,
然后再配置文件中配置包扫描
准备接口 UserDao和EmpDao
1 2 3 public interface EmpDao { int addEmp (Integer empno,String ename,String job) ; }
1 2 3 public interface UserDao { int addUser (Integer userid,String username) ; }
接口实现类
1 2 3 4 5 6 7 8 @Repository public class UserDaoImpl implements UserDao { public int addUser (Integer userid,String username) { System.out.println("userdao add ... ..." ); return 1 ; } }
1 2 3 4 5 6 7 @Repository public class EmpDaoImpl implements EmpDao { public int addEmp (Integer empno,String ename,String job) { System.out.println("empDao add ... ..." ); return 1 ; } }
准备切面
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 @Component @Aspect public class DaoAspect { @Pointcut("execution(* com.msb.dao.*.add*(..))") public void addPointCut () {} @Before("addPointCut()") public void methodBefore (JoinPoint joinPoint) { System.out.println("Before invoked" ); } @After("addPointCut()") public void methodAfter (JoinPoint joinPoint) { System.out.println("After invoked" ); } @AfterReturning( value = "addPointCut()",returning = "res") public void methodAfterReturning (JoinPoint joinPoint,Object res) { System.out.println("AfterReturning invoked" ); } @AfterThrowing( value = "addPointCut()",throwing = "ex") public void methodAfterThrowing (Exception ex) { System.out.println("AfterThrowing invoked" ); } @Around("addPointCut()") public Object methodAround (ProceedingJoinPoint proceedingJoinPoint) throws Throwable { System.out.println("aroundA invoked" ); Object proceed = proceedingJoinPoint.proceed(); System.out.println("aroundB invoked" ); return proceed; } }
AOPXML方式实现 1、创建两个类,增强类和被增强类,创建方法
见之前的代码
2、在spring配置文件中创建两个类对象
1 2 3 <bean id ="userDao" class ="com.com.msb.UserDaoImpl" > </bean > <bean id ="daoAspect" class ="com.com.aspect.DaoAspect" > </bean >
3、在spring配置文件中配置切入点
1 2 3 4 5 6 7 8 9 10 11 12 13 14 <aop:config > <aop:pointcut id ="pointCutAdd" expression ="execution(* com.msb.dao.UserDao.add*(..))" /> <aop:aspect ref ="daoAspect" > <aop:before method ="methodBefore" pointcut-ref ="pointCutAdd" /> <aop:after method ="methodAfter" pointcut-ref ="pointCutAdd" /> <aop:around method ="methodAround" pointcut-ref ="pointCutAdd" /> <aop:after-returning method ="methodAfterReturning" pointcut-ref ="pointCutAdd" returning ="res" /> <aop:after-throwing method ="methodAfterThrowing" pointcut-ref ="pointCutAdd" throwing ="ex" /> </aop:aspect > </aop:config >
Spring_JdbcTemplate概述 JdbcTemplate概述
JdbcTemplate是spring框架中提供的一个对象,是对原始繁琐的Jdbc API对象的简单封装。spring框架为我们提供了很多的操作模板类。例如:操作关系型数据的JdbcTemplate和,操作nosql数据库的RedisTemplate,操作消息队列的JmsTemplate等等。