Spring的学习

IoC是什么?

ioc通过文件配置来实现对象的创建,以及对象的赋值和注入。

   

IoC的依赖

ioc所依赖的包

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

<?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.wiggin</groupId>

<artifactId>aispring</artifactId>

<version>1.0-SNAPSHOT</version>

<packaging>jar</packaging>

<repositories>

<repository>

<id>huaweicloud</id>

<name>huaweicloud</name>

<url>>https://mirrors.huaweicloud.com/repository/maven/</url>

</repository>

</repositories>

<dependencies>

<dependency>

<groupId>org.springframework</groupId>

<artifactId>spring-context</artifactId>

<version>5.0.7.RELEASE</version>

</dependency>

<!-- 简化实体类代码开发-->

<dependency>

<groupId>org.projectlombok</groupId>

<artifactId>lombok</artifactId>

<version>1.18.0</version>

<scope>provided</scope>

</dependency>

   

<dependency>

<groupId>dom4j</groupId>

<artifactId>dom4j</artifactId>

<version>1.1</version>

</dependency>

</dependencies>

   

   

   

   

</project>

@Data:控制数据输出成String类型,以及gettersetter函数

@AllArgsConstructor:快速条件构造函数

@NoArgsConstructor:添加无参构造

1

2

3

4

5

6

7

8

9

10

package Com.wiggin.entity;

@Data

@AllArgsConstructor

@NoArgsConstructor

public class Student {

private Long id;

private String name;

private int age;

private Address address;

   

}

   

配置文件

1.通过配置Bean标签来完成对象的管理

2.id:对象名

3.class:对象的模板类。(交给ioc管理的类必须要有无参构造方法,spring底层通过反射机制来创建对象,调用的是无参构造)

4.对象的成员变量通过property标签完成赋值

5.name:成员变量名,value成员变量值(基本数据类型,String可以直接赋值,引用类型通过ref注入)

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"

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

<bean id="student" class="com.wiggin.entity.Student">

<property name="id" value="1"></property>

<property name="name" value="wiggin"></property>

<property name="age" value="22"></property>

<property name="address" ref="address"></property>

</bean>

<bean id="address" class="com.wiggin.entity.Address">

<property name="id" value="2"></property>

<property name="name" value="vivian"></property>

</bean>

</beans>

   

   

IOC的底层原理

1.读取配置文件,解析XML

2.通过反射机制,实例化配置文件中所配置文件中所有的Bean

   

通过运行时类获取bean

1

2

3

ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");

Student student = (Student) applicationContext.getBean("student");

System.out.println(student);

这种方式存在一个问题,配置文件中一个数据类型的对象只能有一个实例。否则会抛出异常,因为没有唯一的bean

   

   

通过有参构造来创建bean

1

2

3

4

5

6

<bean id="student3" class="com.wiggin.entity.Student">

<constructor-arg name="id" value="3"></constructor-arg>

<constructor-arg name="name" value="bob"></constructor-arg>

<constructor-arg name="age" value="18"></constructor-arg>

<constructor-arg name="address" ref="address"></constructor-arg>

</bean>

其中name可以实省略,但是可以通过下标来确定位置,但顺序必须与成员变量顺序一致

1

2

3

4

5

6

<bean id="student3" class="com.wiggin.entity.Student">

<constructor-arg index="0" value="3"></constructor-arg>

<constructor-arg index="1" value="bob"></constructor-arg>

<constructor-arg index="2" value="18"></constructor-arg>

<constructor-arg index="3" ref="address"></constructor-arg>

</bean>

   

给bean注入集合

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

<bean id="student" class="com.wiggin.entity.Student">

<property name="id" value="1"></property>

<property name="name" value="wiggin"></property>

<property name="age" value="22"></property>

<property name="addresses">

<list>

<ref bean="address"></ref>

<ref bean="address2"></ref>

</list>

</property>

</bean>

<bean id="address" class="com.wiggin.entity.Address">

<property name="id" value="1"></property>

<property name="name" value="科技路"></property>

</bean>

<bean id="address2" class="com.wiggin.entity.Address">

<property name="id" value="2"></property>

<property name="name" value="高新区"></property>

</bean>

   

Scope作用域

Spring管理bean是根据scope来生成的,表示bean的作用域,共有4种,默认是单例模式

1)singleton:单例,表示通过Spring容器获取的bean是唯一的,

2)prototype:原型,表示通过Spring容器获取的bean是不同的

3)request:请求,表示一次Http请求内有效

4)session:回话,表示在一个用户会话内有效

requestsession是适用于web项目,大多数使用单例和原型。

prototype模式当业务代码获取Ioc容器中的bean时(getbean),Spring才去调用无参构造来创建对应的bean。创建一个bean开辟一个空间,避免bean的利用率低的问题,但是也浪费了地址空间。

1

2

3

<bean id="student2" class="com.wiggin.entity.Student" scope="prototype">

<property name="name" value="vivian"></property>

</bean>

singleton模式当ioc容器创建时(new ClassPathXmlApplicationContext("applicationContext.xml"))时,bean已经被创造。多个bean指向同一个地址更加节省空间,但是bean在被创建后的利用率低。

   

Spring的继承

与java的继承不同,java是类层面的继承,子类可以继承父类的父类的内部结构信息,Spring是对象层面的继承,子对象可以继承父对象的属性值。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

<bean id="student2" class="com.wiggin.entity.Student">

<property name="name" value="vivian"></property>

<property name="id" value="2"></property>

<property name="age" value="22"></property>

<property name="addresses">

<list>

<ref bean="address"></ref>

<ref bean="address2"></ref>

</list>

</property>

</bean>

<bean id="stu" class="com.wiggin.entity.Student" parent="student2">

<property name="name" value="wzxsb"></property>

</bean>

   

Spring的继承的关注点在于具体的对象,而不在于类,即不同的两个类的实例化对象可以完成继承,前提是子对象的属于一定是包括父对象的属性。

1

<bean id="user" class="com.wiggin.entity.User" parent="student2"></bean>

   

   

Spring的依赖

与继承类似,依赖也是描述bean和bean之间的一种关系,配置依赖之后,被依赖的bean一定要先被创建。

1

2

3

<bean id="student" class="com.wiggin.entity.Student" ></bean>

   

<bean id="user" class="com.wiggin.entity.User" depends-on="student"></bean>

   

Spring的p命名空间

p命名空间实对ioc/DI的简化操作,使用p命名空间可以更加方便的完成bean的配置以及bean之间的依赖注入

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"

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.wiggin.entity.Student" p:id="1" p:name="张三" p:age="22" p:address-ref="address"></bean>

<bean id="address" class="com.wiggin.entity.Address" p:id="2" p:name="科技路"></bean>

</beans>

   

Spring的工厂方法

ioC通过工厂模式创建bean的方式有两种:

1)静态工厂方法

2)实例工厂方法

静态工厂方法:工厂不需要实例化,直接使用工厂方法

1

2

3

4

5

6

7

8

9

10

11

12

13

package com.wiggin.entity;

   

import lombok.AllArgsConstructor;

import lombok.Data;

import lombok.NoArgsConstructor;

   

@Data

@AllArgsConstructor

@NoArgsConstructor

public class Car {

private long id;

private String name;

}

------

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

package com.wiggin.factory;

   

import com.wiggin.entity.Car;

   

import java.util.HashMap;

import java.util.Map;

   

public class StaticFactory {

private static Map<Long, Car> carMap; // Long id,Car是返回容器

static{

carMap = new HashMap<Long, Car>();

carMap.put(1L,new Car(1L,"sf"));

carMap.put(2L,new Car(2L,"sb"));

}

   

public static Car getCar(long id){

// 根据id提取car信息

return carMap.get(id);

}

}

------

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

package com.wiggin.test;

   

import com.wiggin.entity.Car;

import com.wiggin.factory.StaticFactory;

import org.springframework.context.ApplicationContext;

import org.springframework.context.support.ClassPathXmlApplicationContext;

   

public class Test3 {

public static void main(String[] args) {

// 传统方法

/*Car car = StaticFactory.getCar(1L);

System.out.println(car);*/

// ioc工厂模式

ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-factory.xml");

Car car = (Car)applicationContext.getBean("car");

System.out.println(car);

}

}

------

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"

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

   

<!--用到几个对象配置几个bean-->

<bean id="car" class="com.wiggin.factory.StaticFactory" factory-method="getCar">

<constructor-arg value="2"></constructor-arg>

</bean>

</beans>

实例工厂方法:工厂需要实例化,且工厂方法要依赖工厂

1

2

3

4

5

6

<!-- 配置实例工厂 bean -->

<bean id="carFactory" class="com.wiggin.factory.InstanceCarFactory"></bean>

<!-- 配置实例工厂创建 car -->

<bean id="car2" factory-bean="carFactory" factory-method="getCar">

<constructor-arg value="2"></constructor-arg>

</bean>

   

IoC自动装载(Autowire)

ioC负责创建对象,DI负责完成对象的依赖注入,通过配置Property标签的ref属性来完成,同时Spring提供了另一种更加间接的依赖注入方法,即自动装载,不用手动配置property,IoC容器会自动选择bean完成注入。

   

自动装载有两种方式:

1)byname:通过属性名自动装载

2)bytype:通过属性类型装载

byName:通过属性名自动装载

1

2

3

4

5

6

7

8

9

<bean id="car" class="com.wiggin.entity.Car">

<property name="id" value="1"></property>

<property name="name" value="宝马"></property>

</bean>

<bean id="person" class="com.wiggin.entity.Person" autowire="byName">

<property name="id" value="1"></property>

<property name="name" value="wiggin"></property>

   

</bean>

byType:通过属性类型装载

1

2

3

4

5

<bean id="person2" class="com.wiggin.entity.Person" autowire="byType">

<property name="id" value="1"></property>

<property name="name" value="wiggin"></property>

   

</bean>

   

Spring的AOP

AOP:Aspect Oriented Programming 面向切面编程

AOP 优点:

  1. 降低模块之间的耦合度
  2. 使得系统更容易扩展
  3. 更好的代码服用
  4. 非业务代码更加集中,不分散,使于统一管理
  5. 业务代码更加简介纯粹,不掺杂其他代码的影响

总结:AOP是对面向对象编程的补充,在运行时,动态地将代码切入到类的指定方法、指定位置上的编程思想就是面向切面编程,将不同方法的同一个位置抽象成为一个企鹅面对象,对该企鹅面对象进行编程,这就是AOP。

   

如何使用?

1)创建一个maven工程,pom.xml添加

1

2

3

4

5

6

7

8

9

10

11

12

13

<dependencies>

   

<dependency>

<groupId>org.springframework</groupId>

<artifactId>spring-aop</artifactId>

<version>5.0.7.RELEASE</version>

</dependency>

<dependency>

<groupId>org.springframework</groupId>

<artifactId>spring-aspects</artifactId>

<version>5.0.7.RELEASE</version>

</dependency>

</dependencies>

2)创建一个计算器接口Cal,定义四则运算。

1

2

3

4

5

6

package com.wiggin.utils;

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

}

3)创建接口的实现类impl

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

package com.wiggin.utils.impl;

public class CalImpl implements Cal {

   

public int add(int num1, int num2) {

System.out.println("add方法参数是["+num1+","+num2+"]");

int result = num1 + num2;

System.out.println("add方法的结果是:"+result);

return num1 + num2;

}

   

public int sub(int num1, int num2) {

int result = num1 - num2;

System.out.println("sub方法参数是["+num1+","+num2+"]");

System.out.println("sub方法结果是:"+result);

return result;

}

   

public int mul(int num1, int num2) {

int result = num1 * num2;

System.out.println("mul方法参数是["+num1+","+num2+"]");

System.out.println("mul方法结果是:"+result);

return result;

}

   

public int div(int num1, int num2) {

int result = num1 / num2;

System.out.println("div方法参数是["+num1+","+num2+"]");

System.out.println("div方法结果是:"+result);

return result;

}

}

上述代码种,日志信息和业务逻辑的耦合性很高,不利于系统的维护,使用AOP可以进行优化,如何来实现AOP?实现动态代理方式来实现。

   

给予业务代码找一个代理,打印日志信息的工作交给代理来做,这样的话业务代码就只需要关注自身的业务即可。

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

package com.wiggin.utils;

public class MyInvocationHandler implements InvocationHandler {

   

// 接收委托对象

private Object object = null;

   

// 返回代理对象(委托对象传入代理对象),用这个类创建动态代理类

public Object bind(Object object){

this.object = object;

/*Proxy.newProxyInstance:返回代理实类,

object.getClass()获得委托对象的运行实类,

.getClassLoader()获取类加载器,生成动态实类

object.getClass().getInterfaces()委托对象的功能代理类必须全部拥有,根据它创建功能一样的代理类

this:通过当前类创建对象*/

//

return Proxy.newProxyInstance(object.getClass().getClassLoader(),object.getClass().getInterfaces(),this);

}

   

@Override

// 打印日志结果

/*

method为代理类实现的方法

method.invoke(this.object,objects)通过反射机制调用,this.object是委托对象,objects为传入参数

   

*/

public Object invoke(Object o, Method method, Object[] objects) throws Throwable {

System.out.println(method.getName()+"方法参数是"+ Arrays.toString(objects));

// 委托对象调用自己的方法

Object result = method.invoke(this.object,objects);

System.out.println(method.getName()+"方法的结果是:"+result);

return result;

   

}

   

   

}

以上是通过动态代理来实现AOP的过程,过程复杂,不好理解,Spring框架对AOP进行了封装,使用Spring框架可以用面向对象的思想来实现AOP。

Spring框架种不需要创建InvocationHandler,只需要创建一个切面对象,将所有的非业务代码在切面对象种完成即可,Spring框架底层会自动根据切面类以及目标类生成代理对象。

   

LoggerAspect类

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

package com.wiggin.aop;

@Aspect // 成为切面对象,给予功能

@Component // LoggerAspect对象在ioc里面配置

public class LoggerAspect {

// 传入实体类,切割实体类种的所有方法,在执行业务之前,*为方法的通配符,(..)为参数的通配符

// @Before表示执行的具体时机

@Before("execution(public int com.wiggin.utils.impl.CalImpl.*(..))")

// JoinPoint joinPoint获得目标方法的相关信息

public void before(JoinPoint joinPoint){

// 获取方法joinPoint.getSignature(),方法名getName()

String name = joinPoint.getSignature().getName();

   

//获取参数joinPoint.getArgs()

String args = Arrays.toString(joinPoint.getArgs());

System.out.println(name + "方法的参数是:" + args);

   

   

   

}

}

注意CaLimpl也需要@Component注解,交给IoC容器进行管理

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.wiggin.utils.impl;

@Component

public class CalImpl implements Cal {

   

public int add(int num1, int num2) {

   

int result = num1 + num2;

   

return result;

}

   

public int sub(int num1, int num2) {

int result = num1 - num2;

   

return result;

}

   

public int mul(int num1, int num2) {

int result = num1 * num2;

   

return result;

}

   

public int div(int num1, int num2) {

int result = num1 / num2;

   

return result;

}

}

在Spring-aop.xml种配置AOP

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"

xmlns:aop="http://www.springframework.org/schema/aop"

xmlns:context="http://www.springframework.org/schema/context"

xmlns:tx="http://www.springframework.org/schema/tx"

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

http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd

http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd

http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd">

<!-- 自动扫描 寻找component注解-->

<context:component-scan base-package="com.wiggin"></context:component-scan>

   

<!-- aspect注解生效,为目标类自动生成代理对象-->

<aop:aspectj-autoproxy></aop:aspectj-autoproxy>

</beans>

loggerAspect实现更多业务功能

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

package com.wiggin.aop;

@Aspect // 成为切面对象,给予功能

@Component // LoggerAspect对象在ioc里面配置

public class LoggerAspect {

// 传入实体类,切割实体类种的所有方法,在执行业务之前,*为方法的通配符,(..)为参数的通配符

// @Before表示执行的具体时机

@Before("execution(public int com.wiggin.utils.impl.CalImpl.*(..))")

// JoinPoint joinPoint获得目标方法的相关信息

public void before(JoinPoint joinPoint){

// 获取方法joinPoint.getSignature(),方法名getName()

String name = joinPoint.getSignature().getName();

   

//获取参数joinPoint.getArgs()

String args = Arrays.toString(joinPoint.getArgs());

System.out.println(name + "方法的参数是:" + args);

   

   

   

   

}

@After("execution(public int com.wiggin.utils.impl.CalImpl.*(..))")

public void after(JoinPoint joinPoint){

// 获取方法joinPoint.getSignature(),方法名getName()

String name = joinPoint.getSignature().getName();

System.out.println(name + "方法执行了");

   

}

@AfterReturning(value = "execution(public int com.wiggin.utils.impl.CalImpl.*(..))",returning = "result")

// result交给了 @AfterReturning,然后给了public void afterReturning

public void afterReturning(JoinPoint joinPoint,Object result){

// 获取方法名

String name = joinPoint.getSignature().getName();

System.out.println(name + "方法执行结果是:"+result);

}

   

@AfterThrowing(value = "execution(public int com.wiggin.utils.impl.CalImpl.*(..))",throwing = "expection")

// result交给了 @AfterReturning,然后给了public void afterReturning

public void afterThrowing(JoinPoint joinPoint,Exception expection){

// 获取方法名

String name = joinPoint.getSignature().getName();

System.out.println(name + "方法执行异常:"+expection);

}

}

IoC调用

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

package com.wiggin.test;

   

import com.wiggin.utils.Cal;

import com.wiggin.utils.MyInvocationHandler;

import com.wiggin.utils.impl.CalImpl;

import org.springframework.context.ApplicationContext;

import org.springframework.context.support.ClassPathXmlApplicationContext;

   

public class Test2 {

public static void main(String[] args) {

// 加载配置文件

ApplicationContext applicationContext =new ClassPathXmlApplicationContext("Spring.xml");

   

// 获取代理对象

Cal proxy = (Cal) applicationContext.getBean("test");

proxy.add(1,1);

proxy.div(6,0);

   

}

切面:横切关注点被模块化的抽象对象(对象中相同的非业务代码)

通知:切面对象完成的工作(非业务代码)

目标:被通知的对象,即被切面的对象(非业务代码存在的类)

代理:切面、通知、目标混合之后的对象(一个代理实现非业务代码功能的类)

连接点:通知要插入业务代码的具体位置(代理和目标之间的联系)

切点:AOP通过切点定位到连接点

posted @ 2020-08-01 21:59  wigginess  阅读(170)  评论(0编辑  收藏  举报