Spring基础篇02

1.Spring IoC

1.1 IOC原理

将对象创建交给Spring去管理。

1.2 实现IOC的两种方式

  • IOC配置文件的方式
  • IOC注解的方式

1.3 Spring IOC底层实现原理

在这里插入图片描述
在这里插入图片描述

IOC底层实现使用的技术:

  1. xml配置文件
  2. dom4j解析xml文件
  3. 工厂模式
  4. 反射
    Spring的IoC的底层实现原理是工厂设计模式+反射+XML配置文件。 就拿持久层(也即dao层,数据访问对象)的开发来说,官方推荐做法是先创建一个接口,然后再创建接口对应的实现类。所以,我会以dao层的开发为例来证明Spring的IoC的底层实现原理就是工厂设计模式+反射+XML配置文件。首先,创建一个Userdao接口。
public interface UserDao {
	public void add();
}

创建Userdao接口的一个实现类

public class UserDaoImpl implements UserDao {
    public void add() {
	    balabala......
    }
}

接着,我们在service层中调用dao层

// 接口 实例变量 = new 实现类
UserDao dao = new UserDaoImpl();
dao.add();

这时我们便可发现一个缺点:service层和dao层耦合度太高了,即接口和实现类有耦合(它俩之间的联系过于紧密),一旦切换底层实现类,那么就需要修改源代码,现的这个问题该如何解决呢?解决方法是使用工厂设计模式进行解耦合操作。所以,我们需要创建一个工厂类,在工厂类中提供一个方法,返回实现类的对象。

public class BeanFactory {
    // 提供返回实现类对象的方法
    public static UserDao getUserDao() {
        return new UserDaoImpl();
    }
}

这样,在service层中调用dao层的核心代码就变为了下面的样子。

UserDao dao = BeanFactory.getUserDao();
dao.add();

这样又产生了一个缺点:现在接口和实现类之间是没有耦合了,但是service层和工厂类耦合了。如果真正想实现程序之间的解耦合,那么就需要使用到工厂设计模式+反射+XML配置文件了。所以,我们这里提供一个XML配置文件,并且该配置文件中有如下配置信息。

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

然后再来创建一个工厂类,在工厂类中提供一个返回实现类对象的方法,但并不是直接new实现类,而是使用SAX解析配置文件,根据标签bean中的id属性值得到对应的class属性值,使用反射创建实现类对象。

public class BeanFactory {
    public static Object getBean(String id) {
        // 1.使用SAX解析得到配置文件内容
        // 直接根据id值userDao得到class属性值
        String classvalue = "class属性值";
        // 2.使用反射得到对象
        Class clazz = Class.forName(classvalue);
        UserDaoImpl userDaoImpl = (UserDaoImpl)lazz.newInstance();
        return userDaoImpl;
    }
}

以上就是Spring的IoC的底层实现原理。

2、解释Spring整合Servlet的背后细节

2.1 为什么要在web.xml中配置listener?

a. 这个listener是实现了ServletContextListener , 只要项目一发布,就会得到通知。
b. 因为它要感知到项目到底有没有发布, 以便我们能够立即创建出来工厂。

public class ContextLoaderListener extends ContextLoader implements ServletContextListener {

2.2 为什么要在web.xml中配置context-param?

a. 如果不配置这个param, 那么spring的代码必然会固定到一个位置去找 applicationContext.xml 这个配置文件。
b. 由于这个配置文件,spring早期把命名的权利变得开放了。 也就是这个名字可以随便写。假设是这样,我们不用写这个context-param ,那么 spring必然要到默认| 固定位置去找一个固定名字的xml文件。

2.3 为什么在代码里面采用工具类也能获取到工厂,这个工厂创建出来之后到底放在了哪里?

要推理一下。 看看工厂创建完毕之后,对工厂做了什么操作, 为什么使用工具类可以得到工厂。

其实listener创建出来工厂之后,会把工厂存储到servletcontext作用域里面去。 这主要是方便在任何地方都能取得到这个工厂,而且这个工厂存活的时间还很长。

servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context);

那么既然知道spring是存储到servletcontext去, 那么获取这个工厂,也可以使用以前的手法来获取它。

WebApplicationContext context = (WebApplicationContext) getServletContext().getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE);

但是spring考虑到这个属性的名字太长,程序员记不住,所以自己弄了一个工具类出来,只让我们记住工具方法,也可以得到工厂。

WebApplicationContext context = WebApplicationContextUtils.getWebApplicationContext(getServletContext());

3、IOC的注解开发

3.1 xml 和 注解配置的优缺点

3.11 xml

优点: 代码集中、配置都是集中在一个文件,查找比较方便
缺点: 代码太多。

3.12 注解

优点: 代码少 , 只要在类上面、 方法上 打个注解就OK
缺点: 配置比较分散。
Spring MVC 崇尚的是注解配置 、 Spring Boot 完全摒弃了xml 全部都是用注解.

3.2 ioc注解入门

  1. 导入jar包

spring-aop-xxx.jar //aop联盟包

  1. 导入context约束
<!-- 1. 导入约束 -->
<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">
  1. 打开注解的扫描开关
 <!-- 1. 配置扫描注解的开关 base-package : 如果有多个包要扫描,那么只要写上基础包即可。 -->
  <context:component-scan base-package="com.cao.springIoc.test"/>
  1. 在托管的类上面打上注解,并且给定value值(也就相当于是id值)
@Component(value="us")  
	// Component : 组件 1. 托管这个类  , 2 .给这个类打上个标记  us
	//us 等于 <bean id="us">
	public class UserServiceImpl implements UserService {

3.3 注解创建对象

  1. 注解创建多实例

默认创建的实例还是单例的

@Scope(“prototype”)

  1. 给类打上注解,以便让spring托管它,并且和标识符形成映射关系,都是靠@Component注解来实现
  2. 但是由于web项目采用分层的结构, controller 、 service 、 dao 层,spring为了迎合这种趋势,它也把注解稍微再细分了一下:

@Repository(“userDao”) //持久层
@Service(“us”) //业务逻辑层
@Controller(“action”) //控制层

  1. 指定初始化&销毁方法

@PostConstruct //用于定义指定方法为初始化方法
public void init(){
System.out.println(“调用了UserServiceImpl的init方法~~”);
}

@PreDestroy //用于定义指定方法为销毁时调用的方法
public void destory(){
	System.out.println("调用了UserServiceImpl的destroy方法~~");
}

3.4 属性注入(注解方式)

3.41 注入普通数据

普通数据其实可以不用注解的方式来注入,直接写上赋值即可。

@Value(“北京”)
private String address;

3.42 注入对象数据

3.421 早期的xml注入:
<!--1.  这行代码就是让spring托管这个类,然后给这个类打上一个标识符 
		   		在这个类上面打上注解
		   	-->
		   	<bean id="ud" class="com.cao.dao.impl.UserDaoImpl"></bean>
		   	
		   	<bean id="us" class="com.cao.service.impl.UserServiceImpl">
		   	
		   		<!-- 2. 把ud对应的类实例给注入到userDao中 -->
		   		<property name="userDao" ref="ud"></property>
		   	</bean>
3.422 注解的注入
  1. 在需要注入进来的那个类打上注解,让spring托管它

@Repository(“ud”)
public class UserDaoImpl implements UserDao{

  1. 在成员属性上使用注解来注入对象
 @Resource(name="ud") //根据给定的标识符找到具体的类   name="" ====> ref=""
 @Autowired //自动找到这个接口的实现类然后注入进来
private UserDao userDao ;

如果只有一种实现的情况,又懒得写太多,就可以直接使用 @Autowired 。 但是如果存在多种实现,又想指定具体的某一个实现,那么请使用 @Resource(name=“标识符”)

3.5 混合使用

如果在项目里面使用xml 和 注解 来完成IOC + DI , 那么他们是有分工的, 使用xml来完成IOC , 使用注解来完成DI
xml配置:

<bean id="us" class="com.itheima.service.impl.UserServiceImpl</bean>
	<bean id="ud" class="com.itheima.dao.impl.UserDaoImpl"></bean>

			<!-- 注解扫描开关 -->
<context:component-scan base-package="com.cao"/>

类上使用注解:

@Resource(name="ud")  //直接指定具体的类,注入进来。
	@Autowired //自动装配, 找到这个接口的实现类,然后注入进来 
	private UserDao userDao; 

4.Spring测试

Spring测试其实是整合了以前的Junit, 或者称之为对Junit的封装。

  1. 导入jar包

spring-test-xxx.jar
spring-aop-xxx.jar

  1. 在注解类上声明注解
		@RunWith(SpringJUnit4ClassRunner.class)  // 在这里面升级了以前Junit的运行环境, 在里面兼备了创建工厂的代码
	@ContextConfiguration("classpath:applicationContext.xml")
	public class TestUserService {

  1. 使用属性注入注解,来获取对象
@Autowired
private UserService userService ;

5.AOP

5.1 什么是AOP呢?

AOP(Aspect Oriented Programming,面向切面编程),可以说是OOP(Object Oriented Programing,面向对象编程)的补充和完善。 其实就是在不改动源码的前提下,对原来的功能做升级 、 增强 、 扩展
在这里插入图片描述

5.2 AOP的底层实现原理

aop就是对方法进行升级、扩展、增强。 能够完成这个功能,学到的有、装饰者模式 、 代理模式(静态代理 、动态代理。) , aop的底层采用的是动态代理, 之所以不用前面两个,是因为它们需要我们创建出来代理类、 装饰类 . 动态代理又有两种实现手法, 一种是jdk的动态代理 , 另一种是cglib的动态代理。

  • jdk动态代理

针对真实类(目标类) ,有实现接口。
在这里插入图片描述

		@Test
public  void testJDKProxy(){
	
	//需求: 调用UserServiceImpl的save方法时, 先执行logger里面的log方法,完成对save的功能扩展
	
	//1. 创建真实对象
	final UserService userService  = new UserServiceImpl();
	
	//2. 创建代理对象
	UserService proxyObj =  (UserService) Proxy.newProxyInstance(
			userService.getClass().getClassLoader(),  //类加载器, 真实类用什么, 代理类也跟着用什么
			userService.getClass().getInterfaces(), //真实类实现了什么接口, 代理类也实现接口
			new InvocationHandler() { //这里是new 接口的匿名实现类 。 给我们用来调用真实方法
				
				/*
				 * method : 就是外面调用的方法引用   : save
				 */
				@Override
				public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
					
					System.out.println("~~~~");
					if(method.getName().equals("save")){
						Logger.log();
					}

					/*	正向调用
					 * userService.save();
					userService.update();
					userService.delete();
					*/
					
					/*
					 * 反射手法调用
					 * 参数一:obj :真实对象
					 * 参数二: args : 外面调用的方法参数。
					 */
					return method.invoke(userService , args);
					//return null;
				}
			});
	
	//3. 调用方法
	proxyObj.save(); //userService.save();
	/*proxyObj.update();
	proxyObj.delete();*/
	
}
  • cglib的动态代理

针对真实类(目标类) ,没有实现接口。 它就是一个普通类。

在这里插入图片描述

@Test
public void testCglibProxy(){
	
	//1. 创建真实对象
	final ProductService productServcie  = new ProductService();
	
	//2. 创建代理 enhancer 不是代理对象,由它创建代理
	Enhancer enhancer = new Enhancer();
	
	//设置父类是谁
	enhancer.setSuperclass(ProductService.class);
	
	//设置回调 , 就是用于调用真实对象的方法
	enhancer.setCallback(new MethodInterceptor() {
		
		@Override
		public Object intercept(Object arg0, Method arg1, Object[] arg2, MethodProxy arg3) throws Throwable {
			
			System.out.println("~~!~~!");
			Logger.log();
			//productServcie.save();
			return arg1.invoke(productServcie, arg2);
		}
	});
	
	//得到代理对象
	ProductService proxyObj = (ProductService) enhancer.create();
	
	proxyObj.save();
}

5.3 AOP 的术语

在这里插入图片描述

5.4 AOP 入门

  1. 定义业务逻辑类
public class UserServiceImpl implements UserService {
	@Override
	public void save() {
		System.out.println("调用了UserServiceImpl 的 save方法");
	}
}
  1. 定义增强类
public class Logger {
public static void log(){
		System.out.println("输出日志了~~");
	}
}
  1. 导入jar包
    a. 导入 spring必须的jar
    b. 额外导入:

spring-aop-xx.jar,spring-aspect-xx.jar

面向切面过程中,Spring AOP是遵循了AOP联盟的规范实现的,所以需要有AOP联盟的接口包
aopalliance-x.x.jar,接口包依赖aspectjweaver-x.x.x.jar

  1. 导入约束
    要导入aop的约束
  2. 让spring托管 业务逻辑类 和 增强类
<bean id="us" class="com.itheima.service.impl.UserServiceImpl" ></bean>
<bean id="logger" class="com.itheima.util.Logger" ></bean>
  1. 配置AOP
 <!-- 下面的配置核心,就是把logger里面的log方法应用到us里面的save方法中 -->
<!-- 开始aop的配置 -->
    <aop:config>
    	<!-- 就是用于表示,要对哪一个方法进行增强。 其实就是定义一种规则,然后spring根据这种规则就能够找到对应的方法
    	后面将要对这些找到的方法进行增强 
    	
    		execution(* com.xyz.myapp.service.*.*(..))
    		execution  : 固定写法,表示执行
    		
    		第一个*  : 表示任意返回值
    		com.xyz.myapp.service : 表示这个包
    		第二个* : 表示上面这个包的任意类
    		第三个* 表示上面找到的类的任意方法
    		(..) : 表示任意参数
    	spring根据这个表达式已经找到了具体对应的方法。
    	-->
    	<aop:pointcut expression="execution(* com.itheima.service.impl.*.*(..))" id="savePointCut"/>
    	
    	<!-- 还缺少,把什么方法应用到上面找到的那些方法之上,去完成增强
    	
    		连起来的意思就是: 把一个叫做logger的这个bean 里面的一个方法叫做 log 的方法,用到 一个叫做savePointCut 切入点找到
    		的那些方法上面去,执行前置增强
    	 -->
    	
    	<aop:aspect ref="logger">
    	
    		<!-- 定义前置增强 
    			根据上面的表达式找到的切面,其实就是找到的那些方法,给他们应用前置增强, 把一个叫做log的方法用到他们前面去。
    		-->
    		<aop:before method="log" pointcut-ref="savePointCut"/>
    		</aop:aspect>
    </aop:config>

5.5 AOP 增强

配置aop

<aop:config>
<!-- 配置切入点 spring根据这个规则,找到具体的方法  
		xml方式不好控制具体哪一个方法
	-->
	<aop:pointcut expression="execution(* com.itheima.service.impl.*.save(..))" id="pointCut01"/>
<!-- 配置切入点 spring根据这个规则,找到具体的方法  
		xml方式不好控制具体哪一个方法
	-->
	<aop:pointcut expression="execution(* com.itheima.service.impl.*.save(..))" id="pointCut01"/>
<!-- 这个是对事务操作时,才有用 -->
	<!-- <aop:advisor advice-ref=""/> -->
	
	<aop:aspect ref="logger">
		<!-- 前置增强 -->
		<!-- <aop:before method="log" pointcut-ref="pointCut01"/> -->
		
		<!-- 后置增强 -->
		<!-- <aop:after method="log" pointcut-ref="pointCut01"/> -->
		
		<!-- 环绕增强 -->
		<!-- <aop:around method="around" pointcut-ref="pointCut01"/> -->
		
		<!-- 异常增强 -->
		<!-- <aop:after-throwing method="log" pointcut-ref="pointCut01"/> -->
		
		<!-- 最终增强 -->
		 <aop:after-returning returning="result" method="log02"  pointcut-ref="pointCut01"/>
	</aop:aspect>

6.总结

  1. IOC的注解

在这里插入图片描述

  1. Spring测试
    在这里插入图片描述
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

CANDH

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值