一、Spring介绍

Spring 是位于业务逻辑层的框架。 优点很多(无缝对接前后层的框架、提供AOP的支持 , 和以前的 Sstruts 、 Hibernate 组合成了一套框架组合 SSH 。现在是和Spring MVC 、 MyBatis 成了新的组合 SSM) 。 spring里面包含两个核心 : IOC + AOP

  • IOC

IOC 的全称是 Inversion Of Control 翻译过来是控制反转的意思。 通俗的说: 就是把对象的创建工作交给spring来完成。

	以前创建对象 :  new 类(); ----> 把工作交给spring ----> 问spring要对象即可
  • AOP

AOP为Aspect Oriented Programming的缩写,意为:面向切面编程

二、IOC演变

创建对象的发展历史

  • 早期直接new对象

icon

  • 演变成工厂

icon

  • spring ioc托管

icon

三、Spring入门

1. 入门案例

  1. 添加依赖
 compile 'org.springframework:spring-context:4.3.17.RELEASE'
  1. 编写业务逻辑类
public interface UserService {
	void save();
}

public class UserServiceImpl implements UserService {
	@Override
	public void save() {
		System.out.println("调用UserServiceImpl的save方法~~");
	}
}
  1. 创建配置文件 applicationContext.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="userService" class="com.itheima.service.impl.UserServiceImpl"/>

</beans>
  1. 在代码里面通过工厂获取对象
public class MainTest {
    @Test
    public void testSave(){
        ClassPathXmlApplicationContext context = new 
            	ClassPathXmlApplicationContext("applicationContext.xml");
        UserService userService = (UserService) context.getBean("userService");
        userService.save();
        context.close();
    }
}

2. 配置详解

1. xml解释

 <!-- bean标签用来托管具体类,也就是告诉spring工厂,我们让它帮忙创建谁的实例对象 
    	
    	id | name: 唯一标识符,不建议写一样的内容
    	class : 全路径
    	
    	默认创建的实例是单例.
    		如果想要做成多例,那么请使用scope属性,里面给值prototype ,
    		默认是单例 ==== singleton
    -->

<bean id="us" class="com.itheima.service.impl.UserServiceImpl" scope="prototype"></bean>  

2. 代码解释

//2. 让spring创建对象 问spring的工厂要对象
//创建工厂,然后告诉工厂,配置文件在哪里。 工厂就会解析这个xml文件,进而得出us  --- UserServiceImpl的对应关系
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
	
//问工厂要对象
UserService userService =(UserService) context.getBean("us");

userService.save();

//关闭工厂,一般不会调用这个方法。
((AbstractApplicationContext) context).close();

四、IOC

此处使用两种方式给大家介绍, xml注解方式

1. xml方式

  • 代码:
public class UserServiceImpl implements UserService {
	@Override
	public void save() {
		System.out.println("调用UserServiceImpl的save方法~~");
	}
}
  • xml配置
 <bean id="us" class="com.itheima.service.impl.UserServiceImpl"></bean>

2. 注解方式

即便使用注解,也还是需要在配置文件里面声明注解的开关

  • 代码
@Compoment
public class UserServiceImpl implements UserService {
	@Override
	public void save() {
		System.out.println("调用UserServiceImpl的save方法~~");
	}
}
  • xml配置
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
    
	<!-- 打开扫描开关 -->
    <context:component-scan base-package="com.itheima"/>
</beans>
  • 细节
  1. 为了迎合三层结构的趋势,Spring 为每一个层级制定了各自的注解,

​ controller层使用 @Controller

​ service层使用@Service

​ dao层使用@Repository

  1. 如果需要给指定的bean添加标识,可以使用注解属性追加 如: @service("userService")

  2. 默认情况下生成的实例还是单例,如果需要做成多例,需要额外加上 @Scope("prototype")

五、依赖注入

DI : 全称是 dependency Injection 翻译过来是依赖注入 本意是在实例化对象的时候,对它里面的成员属性进行值的注入。

public class UserServiceImpl{

​ private String address;

}

对这个address进行赋值: 有几种方式: 两种

  1. 有参构造

  2. set方法

    依赖注入,走的就是上面的这两种。 如果使用注解注入,使用的是反射。

1. xml方式

xml方式需要在配置文件中配置,以完成值的注入,提供两种方法, 有参构造和set方法。

a. 有参构造

  • 代码
public class UserServiceImpl implements UserService {
	
	private String address;
	
	//必须走有参构造
	public UserServiceImpl(String address) {
		this.address = address;
	}

	@Override
	public void save() {
		System.out.println("调用UserServiceImpl的save方法~~" + address);
	}
}
  • xml配置
<bean id="us" class="com.itheima.service.impl.UserServiceImpl" >
      <!-- 
      	constructor-arg : 一旦配置,就指定了spring的工厂走的是 有参构造。
      	name:说的是属性名称
      	value: 注入的值 -->
	<constructor-arg name="address" value="深圳"></constructor-arg>
 </bean>  

b. set方法

  • 代码
public class UserServiceImpl implements UserService {
	
	private String address;
	
	public void setAddress(String address) {
		this.address = address;
	}

	@Override
	public void save() {
		System.out.println("调用UserServiceImpl的save方法~~" + address);
	}
}
  • xml配置
<bean id="us" class="com.itheima.service.impl.UserServiceImpl" >
      
  <!-- 	property标签对应的是代码里面set方法
     name: 属性的名称
     value: 要注入的值 -->
    <property name="address" value="深圳"></property>
</bean>

c. 注入集合类型

1. 数组

代码:

	private String [] address;
	
	public void setAddress(String [] address) {
		this.address = address;
	}

xml:

	 <property name="address">
		<array>
		    <value>北京1</value>
		    <value>北京2</value>
		    <value>北京3</value>
		    <value>北京天安门,我爱北京</value>
		</array>
	</property> 

2. list

代码:

	private List  address;

	public void setAddress(List  address) {
		this.address = address;
	}

xml:

	<property name="address">
		<list>
  			<value>北京11</value>
  			<value>北京22</value>
  			<value>北京33</value>
  			<value>北京天安门,我爱北京44</value>
  		</list>
	</property>

3. map

代码:

	private Map<String , Object>  address;

	public void setAddress(Map<String , Object>  address) {
		this.address = address;
	}

xml:

	<!-- map集合 -->
	<property name="address">
			<map>
  			<entry key="地址1" value="北京1"/>
  			<entry key="地址2" value="北京2"/>
  			<entry key="地址3" value="北京3"/>
  			<entry key="地址4" value="北京4"/>
  		</map>
	</property>

d. 注入 对象类型

在某一个类当中,持有另一个类的引用,我们需要让spring创建这个类的引用,进而通过调用set方法来完成注入

  • 代码
public class UserServiceImpl implements UserService {

		private UserDao userDao;// = new UserDaoImpl(); 体现了两层意思: a. new 某一个类的实例, b. 做出来的实例,赋值给dao.
		
		public void setUserDao(UserDao userDao) {
			this.userDao = userDao;
		}
		
		@Override
		public void save() {
			System.out.println("调用UserServiceImpl的save方法~~" );
			//UserDao userDao  = new UserDaoImpl();
			userDao.save();
		}
	}
  • xml
<!-- 让spring托管这个UserDaoImpl ,以便它能够创建该类的实例, 是为了完成下面的注入 -->
<bean id="ud" class="com.itheima.dao.impl.UserDaoImpl"></bean>
        
<bean id="us" class="com.itheima.service.impl.UserServiceImpl" >
      
     	<!-- 指定了UserServiceImpl里面有一个属性叫做userDao ,它需要被注入, 注入的数据来自于 ref 里面声明的ud
      	spring会拿着ud 找到对应的bean, 接着做出来实例,然后注入给userDao -->
   <property name="userDao" ref="ud"></property>
</bean>

2. 注解方式

依赖注入使用注解来实现 , DI的注解一般使用两个 @Resource & @Autowired

  • 常用的注解就两个 @Resource & @Autowired

@Resource(name="ud") 根据给定的标记找到对应的类,创建对象,注入进来。

@Autowired 自动装配,会找到对应的实现类创建对象,注入进来。但是如果存在多个实现,那么会抛出异常

@Repository("ud")
public class UserDaoImpl implements UserDao {
}


public class UserServiceImpl implements UserService {
	
	
	@Resource(name="ud") //spring拿着ud找到具体的类,然后创建实例,注入进来。
	private UserDao userDao;

	...
}

----------------------------------------------------------

public class UserServiceImpl implements UserService {
	
	
	@Autowired  //自动装配 根据注入的接口类型找到对应的实现类,注入进来。
	private UserDao userDao;

	...
}

三、 AOP

1. 什么是AOP ? 它有什么用?

AOP(Aspect Oriented Programming,面向切面编程), 可以说是OOP(Object Oriented Programing,面向对象编程)的补充和完善。在不改动源码的前提下,对原有的功能进行扩展 | 升级

icon

2. AOP的底层原理

在java的世界里,能够不改动源码,但是又能做出扩展|增强的,不多。有装饰者模式, 有代理模式(静态代理 + 动态代理。) aop的底层采用的是: 动态代理。之所以不用装饰者模式或者静态代理,那是因为这里两种方式,都需要我们写实实在在的扩展类(装饰类 + 代理类) 。

  • 代理回顾

icon

3. 动态代理的实现方式

icon

  • 基于JDK的方式

针对的是被代理类有实现某一个接口,底层创建接口的另一个实现类作为代理类

//jdk动态代理
@Test
public void testJDKPorxy(){
	
	//UserService userService = new UserServiceImpl();
	//userService.save();
	
	//1. 先创建真实对象
	final UserService userService = new UserServiceImpl();
	
	//2. 创建代理对象
	UserService proxyObj = (UserService) Proxy.newProxyInstance(
			userService.getClass().getClassLoader(),  //类加载器,真实类用什么,代理类就用什么
			userService.getClass().getInterfaces(),  //真实类实现什么接口,代理类也实现什么接口
			new InvocationHandler() {//回调函数
				
				@Override
				public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
					System.out.println("invoke~");
					//userService.save();
					
					if(method.getName().equals("save")){
						Logger.log();
					}
					
					//以不变应万变。 反射调用
					return method.invoke(userService, args);
				}
			}); 
	
	//3. 让代理对象干活
	proxyObj.save();  //代理对象。save()  ----> 真实对象.save();
}
  • 基于Cglib动态代理

如果真实类是一个普通类,没有实现接口,那么就采用这种方式, 创建出来真实类的子类作为代理类。

//cglib动态代理
@Test
public void testCglibPorxy(){
	//1. 一定要有真实对象
	final ProductService productService = new ProductService();
	//2. 创建代理
	Enhancer  enhancer = new Enhancer();
	//设置父类是谁
	enhancer.setSuperclass(ProductService.class);
	//设置回调
	enhancer.setCallback(new MethodInterceptor() {
		
		/*
		 * arg0 :代理对象
		 * arg3 : 方法的代理 
		 * 
		 * 一般这两不用。
		 * 
		 * arg1 : 方法引用
		 * arg2 :参数
		 */
		@Override
		public Object intercept(Object arg0, Method arg1, Object[] arg2, MethodProxy arg3) throws Throwable {
			Logger.log();
			return arg1.invoke(productService, arg2);
		}
	});
	
	//创建代理对象
	ProductService proxyObj = (ProductService) enhancer.create();
	proxyObj.save();
	
}

3. AOP术语

04

4.AOP的入门

Spring的AOP其实已经准备好了创建代理的代码。只是不知道的是要创建谁的代码。哪些方法需要被增强。我们需要通过配置的形式告诉spring。

  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. 添加依赖
    compile 'org.springframework:spring-context:4.3.17.RELEASE'

	//aspectJ 依赖包。
    compile group: 'org.aspectj', name: 'aspectjweaver', version: '1.8.10'
  1. xml中配置

要导入aop的约束

让spring托管 业务逻辑类 和 增强类
<bean id="us" class="com.itheima.service.impl.UserServiceImpl" ></bean>
<bean id="logger" class="com.itheima.util.Logger" ></bean>


配置AOP

<!-- 2. 开始配置aop -->
	<aop:config>
	
		<!-- 配置切入点  expression 表达式 '
			execution(* com.xyz.myapp.service.*.*(..))
			
			execution 固定写法
			第一个* 代表任意返回值
			com.xyz.myapp.service : 包名
			第二个* 包下的任意类
			第三个* 类中的任意方法
			(..) : 任意参数
			
			
		saveUser
		saveOrder
		-->
		<aop:pointcut expression="execution(* com.itheima.service.impl.*.*(..))" id="aa"/>
		
		
		<!-- 配置增强 
		
			根据aa的表达式找到的方法,都给他们做前置增强,增强的功能是log方法
		-->
		<aop:aspect ref="logger"> 
			<aop:before method="log" pointcut-ref="aa"/>
		</aop:aspect>
	</aop:config>

5. AOP 增强

  <aop:config>

   <aop:pointcut expression="execution(* com.itheima.service.impl.*.*(..))" id="pointcut01"/>
   		
   		
 	<!-- 以后去企业写代码,真正用aop来扩展一个功能,比较少。 除非是我们想扩展第三方jar包。 
   		aop的思想无处不在:  struts 拦截器 (就是AOP) -->
   		
   		<!-- 配置切面aspect -->
   		<aop:aspect ref="logger">
   			<!-- 前置增强 -->
   			<!-- <aop:before method="log" pointcut-ref="pointcut01"/> -->
   			
   			<!-- 最终增强 -->
   			<!-- <aop:after method="log" pointcut-ref="pointcut01"/> -->
   			
   			<!-- 后置增强 -->
   			<!-- <aop:after-returning method="log" pointcut-ref="pointcut01"/> -->
   			
   			<!-- 异常增强 -->
   			<!-- <aop:after-throwing method="log" pointcut-ref="pointcut01"/> -->
   			
   			<!-- 环绕增强 -->
   			<!-- <aop:around method="around" pointcut-ref="pointcut01"/>  -->
   			
   			
   			<aop:before method="log" pointcut-ref="pointcut01"/>
   			<aop:after-returning method="log" pointcut-ref="pointcut01"/>
   			
   		</aop:aspect>
   		
   
   </aop:config>