Spring 简介(02)
7.Spring EL 表达式(了解)
Spring Expression Language,Spring表达式语言,简称SpEL。支持运行时查询并可以操作对象图。
和JSP页面上的EL表达式,SpEL根据JavaBean风格的getXxx()、setXxx()方法定义的属性访问对象图,完全符合我们熟悉的操作习惯。
7.1 基本语法
SpEL使用**#{…}**作为定界符,所有在大框号中的字符都将被认为是SpEL表达式。
7.2 使用字面值
7.3 引用其他bean
7.4 引用其他bean的属性值作为自己某个属性的值
7.5 调用非静态方法
7.6 调用非静态方法
<bean id="car" class="com.atguigu.pojo.Car">
<property name="name" value="粪叉子"/>
<property name="carNo" value="京A43242"/>
</bean>
<bean id="person" class="com.atguigu.pojo.Person">
<property name="id" value="#{1}"/>
<!-- <property name="name" value="#{laoxu}"/>-->
<!-- <property name="name" value="#{'laoxu'}"/>-->
<!-- 调用静态方法 -->
<!-- <property name="name" value="#{car.noStaticFun()}"/>-->
<!-- 调用非静态方法 -->
<property name="name" value="#{T(com.atguigu.pojo.Car).staticFun()}"/>
<property name="salary" value="#{342.2}"/>
<property name="car" value="#{car}"/>
</bean>
7.7 运算符
①算术运算符:+、-、*、/、%、^
②字符串连接:+
③比较运算符:<、>、==、<=、>=、lt、gt、eq、le、ge
④逻辑运算符:and, or, not, |
⑤三目运算符:判断条件?判断结果为true时的取值:判断结果为false时的取值
⑥正则表达式:matches
8.注解功能(重点)
8.1 注解Dao Service Controller组件
实验32 : 通过注解分别创建Dao , Service , Controller
Spring 配置中bean的常用注解有
@Component 配置Web层 , Service层 , Dao层之外的对象
@Controller 配置Web层的组件
@Service 配置Service层
@Repository 配置Dao层
@Scope 配置Bean的作用域(单例 , 多例),用于类上 , 也可以用 于方法上, 当前方法必须有@Bean
@PostConstruct init-method
@PreDestory destory-method
@Value(“abc”) 给基本类型注入
@Value("${user}") 读取配置文件中的key得到Value给基本类型注入
@Value("${user:root}") 读取配置文件中的key如果没有key , 将root进行 注入
@Configuraction 表明当前类是一个配置文件类
@ComponentScan(basePackages={}) 扫描其他包下的注解
@Import 导入其他配置文件类
@Bean 将当前方法返回值放入容器内 , bean的id是方法名
代码 :
application.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 https://www.springframework.org/schema/context/spring-context.xsd">
<!-- 扫描其他注册的包 -->
<context:component-scan base-package="com.atguigu"/>
</beans>
controller :
import org.springframework.stereotype.Controller;
/**
* @Controller 注解的作用是
* 相当于 <bean class="com.atguigu.controller.PersonController" id="personController"/>
*/
@Controller
public class PersonController {
}
dao :
/**
* @Repository 注解的作用是 :
* 相当于<bean = "com.atguigu.pojo.Impl.PersonDaoImpl" id = "personDaoImpl"/>
*/
@Repository
public class PersonDaoImpl implements PersonDao {
}
pojo :
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
/**
* @Component : 注解的作用是:
* 相当于 : <bean class="com.atguigu.pojo.Person" id="person">
*
* @Scope("prototype") prototype : 多例
* @Scope("singleton") singleton : 单例 默认
*/
@Component
@Scope("singleton")
public class Person {
}
service :
import com.atguigu.service.PersonService;
import org.springframework.stereotype.Service;
/**
* @Service 注解的作用是 :
* 相当于<bean class="com.atguigu.service.Impl.PersonServiceImpl" id="personServiceImpl"/>
*/
@Service
public class PersonServiceImpl implements PersonService {
}
8.2 指定扫包时过滤的内容
实验33:使用context:include-filter指定扫描包时要包含的类
实验34:使用context:exclude-filter指定扫描包时不包含的类
<context:include-filter /> 设置包含的内容
注意:通常需要与use-default-filters属性配合使用才能够达到“仅包含某些组件”这样的效果。即:通过将use-default-filters属性设置为false,
<context:exclude-filter /> 设置排除的内容
类别 示例 说明 annotation com.atguigu.XxxAnnotation 过滤所有标注了XxxAnnotation的类。这个规则根据目标组件是否标注了指定类型的注解进行过滤 assignable com.atguigu.BaseXxx 过滤所有BaseXxx类的子类。这个规则根据目标组件是否是指定类型的子类的方式进行过滤。 aspectj com.atguigu.*Service+ 所有类名是以Service结束的,或这样的类的子类。这个规则根据AspectJ表达式进行过滤。 regex com.atguigu.anno.* 所有com.atguigu.anno包下的类。这个规则根据正则表达式匹配到的类名进行过滤。 custom com.atguigu.XxxTypeFilter 使用XxxTypeFilter类通过编码的方式自定义过滤规则。该类必须实现org.springframework.core.type.filter.TypeFilter接口
自定义排除 :
<!--
context:component-scan 配置包扫描
base-package="com" 指定要扫描哪个包,以及它的子包都会扫描
-->
<context:component-scan base-package="com.atguigu">
<!--
type="annotation" 排除过滤 排除注解, 并且包含子注解
type : 是指过滤的类型
expression : 过滤的表达式
-->
<context:exclude-filter type="annotation"
expression="org.springframework.stereotype.Repository"/>
</context:component-scan>
</beans>
自定义包含 :
<!--
context:component-scan 配置包扫描
base-package="com" 指定要扫描哪个包,以及它的子包都会扫描
-->
<context:component-scan base-package="com.atguigu" use-default-filters="false">
<!--
context:include-filter 标签是自定义包含 , 必须use-default-filters="false"一起使用
type="annotation" 排除过滤
expression : 过滤的表达式
-->
<context:include-filter type="annotation"
expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
注:
1 . @Controller @Service 和 @Repository 都继承了 Component
8.3 使用注解@Autowrite自动装配
实验35:使用@Autowired注解实现根据类型实现自动装配★
@Autowired 注解 会自动的根据标注的对象类型在Spring容器中查找相对应的类。如果找到,就自动装配。
使用@Autowired注解,不需要get/set方法
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;
/**
* @Service 注解的作用是 :
* 相当于<bean class="com.atguigu.service.Impl.PersonServiceImpl" id="personServiceImpl"/>
*/
@Service
public class PersonServiceImpl implements PersonService {
/**
* @Autowired : 将容器中bean给变量赋值
* 1. 它会先从Spring容器中按类型找到并注入
*/
@Autowired(required = false)
private PersonDao personDao;
@Override
public String toString() {
return "PersonServiceImpl{" +
"personDao=" + personDao +
'}';
}
}
8.4 多个同类的bean如何自动装配
实验36:如果资源类型的bean不止一个,默认根据@Autowired注解标记的成员变量名作为id查找bean,进行装配★
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;
/**
* @Service 注解的作用是 :
* 相当于<bean class="com.atguigu.service.Impl.PersonServiceImpl" id="personServiceImpl"/>
*/
@Service
public class PersonServiceImpl implements PersonService {
/**
* @Autowired : 将容器中bean给变量赋值
* 1. 它会先从Spring容器中按类型找到并注入
* 2. 当发现查找多个时候用 @Qualifier("personDaoImpl") 进行标识
* 或者用@Resource(name = "personDaoImpl") 进行标识
*
*/
@Autowired(required = false)
@Qualifier("personDaoImpl1")
private PersonDao personDao;
/* @Resource(name = "personDaoImpl")
private PersonDao personDao;*/
@Override
public String toString() {
return "PersonServiceImpl{" +
"personDao=" + personDao +
'}';
}
}
8.5 使用@Qualifier装配指定的id的bean对象
实验37:如果根据成员变量名作为id还是找不到bean,可以使用@Qualifier注解明确指定目标bean的id★
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;
/**
* @Service 注解的作用是 :
* 相当于<bean class="com.atguigu.service.Impl.PersonServiceImpl" id="personServiceImpl"/>
*/
@Service
public class PersonServiceImpl implements PersonService {
/**
* @Autowired : 将容器中bean给变量赋值
* 1. 它会先从Spring容器中按类型找到并注入
* 2. 当发现查找多个时候用 @Qualifier("personDaoImpl") 进行标识
* 或者用@Resource(name = "personDaoImpl") 进行标识
*
*/
@Autowired(required = false)
@Qualifier("personDaoImpl")
private PersonDao personDao;
@Override
public String toString() {
return "PersonServiceImpl{" +
"personDao=" + personDao +
'}';
}
}
8.6 @Autowired注解的required属性作用
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;
/**
* @Service 注解的作用是 :
* 相当于<bean class="com.atguigu.service.Impl.PersonServiceImpl" id="personServiceImpl"/>
*/
@Service
public class PersonServiceImpl implements PersonService {
/**
* @Autowired : 将容器中bean给变量赋值
* 1. 它会先从Spring容器中按类型找到并注入
* 2. 当发现查找多个时候用 @Qualifier("personDaoImpl") 进行标识
* 或者用@Resource(name = "personDaoImpl") 进行标识
* 3. 当 @Qualifier("personDaoImpl1") 可以用@Autowired(required = false) 进行标识允许为null
*
*/
@Autowired(required = false)
@Qualifier("personDaoImpl1")
private PersonDao personDao;
/* @Resource(name = "personDaoImpl")
private PersonDao personDao;*/
@Override
public String toString() {
return "PersonServiceImpl{" +
"personDao=" + personDao +
'}';
}
}
8.7 @AutoWired和@Qualifier在方法上使用
实验38:在方法的形参位置使用@Qualifier注解
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.stereotype.Service; /** * @Service 注解的作用是 : * 相当于<bean class="com.atguigu.service.Impl.PersonServiceImpl" id="personServiceImpl"/> */ @Service public class PersonServiceImpl implements PersonService { /** * @Autowired : 将容器中bean给变量赋值 * 1. 它会先从Spring容器中按类型找到并注入 * 2. 当发现查找多个时候用 @Qualifier("personDaoImpl") 进行标识 * 或者用@Resource(name = "personDaoImpl") 进行标识 * 3. 当 @Qualifier("personDaoImpl1") 可以用@Autowired(required = false) 进行标识允许为null * */ @Autowired(required = false) @Qualifier("personDaoImpl") private PersonDao personDao; /* @Resource(name = "personDaoImpl") private PersonDao personDao;*/ /** * @Autowired 修饰在方法上会在当前bean初始化后执行 * 被@Autowired 修饰的方法的参数 , 会自定注入 , (参数会自动去容器中查找类型相同的id) * 1:当有多个参数是,匹配变量名称 与bean 的id * 2:@Qualifier("personDaoImpl")可以写在方法的参数中,去指定bean的id给参数注入 */ @Autowired public void show(@Qualifier("personDaoImpl") PersonDao personDao){ System.out.println("show() 方法执行" + personDao); } @Override public String toString() { return "PersonServiceImpl{" + "personDao=" + personDao + '}'; } }
8.8 Spring的专有测试
@ContextConfiguration 配置Spring容器需要的配置文件路径
@RunWith 配置使用Spring的扩展junit测试
官方觉得使用Junit测试在编写测试代码的时候,过于繁锁。为了方便测试,并且很好的使用上Spring的一些特性和功能。就对
Junit测试进行了扩展。
1 先导入Spring对junit扩展后的jar包
2 配置两个注解
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
/**
* @RunWith 表示使用Spring 扩展的Junit测试类来测试代码
* @ContextConfiguration : 注解的作用是指定Spring容器需要的配置文件路径
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:bean.xml")
public class SpringTest {
@Autowired
private PersonService personService;
@Test
public void test(){
System.err.println(personService);
}
}
8.9 纯注解
配置
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.*;
/**
* @Configuration == 当前类就是一个Spring的配置文件去使用
* @ComponentScan == <context:comp-scan></context:comp-scan>
* @Import == <import resource =""/> 导入其它配置文件类
* @PropertySource:加载外置properties配置文件 <context:property-placeholder></context:property-placeholder>
*
*/
@Configuration
@ComponentScan(basePackages = {"com.atguigu"})
@Import({SpringConfig01.class})
@PropertySource(value = "classpath:jdbc.properties")
public class SpringConfig {
/*
* @Value("${读取配置文件的key}") 给变量注入
* 写字符串给变量注入
*/
@Value("${user}")
private String name;
/**
* jdbc.password 配置文件的key,当读不到的时候,将root给变量注入
*/
@Value("${password:root}")
private String password;
/**
* <bean id = "" class = ""></bean>
* @Bean : 将当前方法的返回值放入容器中 , bean的id是 当前方法名首字母小写
* @Bean("abc") : 当前bean的id 是 abc
* @Scope : 还可以指定bean的作用域
* @Bean : 修饰的方法也会进行自动注入
* @param personDao
* @return
*/
@Bean
public Person person(@Qualifier("personDaoImpl") PersonDao personDao){
System.out.println("name :" + name);
System.out.println("password : " + password);
return new Person();
}
}
测试 :
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
/**
* @RunWith 表示使用Spring 扩展的Junit测试类来测试代码
* @ContextConfiguration : 注解的作用是指定Spring容器需要的配置文件路径
* classes : 放入配置文件类
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = SpringConfig.class)
public class SpringTest {
@Autowired
private PersonService personService;
@Autowired
private Person person;
@Test
public void test(){
// System.err.println(personService);
System.err.println(person);
}
}
9.AOP切面编程
9.1 什么是AOP
AOP是面向切面编程。全称:Aspect Oriented Programming
面向切面编程指的是:程序是运行期间,动态地将某段代码插入到原来方法代码的某些位置中。这就叫面向切面编程。
9.2 一个简单计算数功能加日记
interface :
package com.atguigu.service;
public interface Calculation {
int add(int num1, int num2);
int add(int num1, int num2, int num3);
int div(int num1, int num2);
}
Calculator 实现类接口:
package com.atguigu.service.Impl;
import com.atguigu.service.Calculation;
import com.atguigu.util.LogUtils;
public class Calculator implements Calculation {
@Override
public int add(int num1, int num2) {
int result = 0;
try {
LogUtils.logBefore("add",num1 ,num2);
result = num1 + num2;
LogUtils.logAfterReturning("add",result);
}catch (Exception e){
LogUtils.logAfterThrowing("add",e);
}
return result;
}
@Override
public int add(int num1, int num2, int num3) {
int result = 0;
try {
LogUtils.logBefore("add",num1,num2,num3);
result = num1 + num2 + num3;
LogUtils.logAfterReturning("add",result);
} catch (Exception e){
LogUtils.logAfterThrowing("add",e);
}
return result;
}
@Override
public int div(int num1, int num2) {
int result = 0;
try {
LogUtils.logBefore("div",num1,num2);
result = num1 / num2;
LogUtils.logAfterReturning("div",result);
} catch (Exception e){
e.printStackTrace();
LogUtils.logAfterThrowing("div",e);
}
return result;
}
}
util:
package com.atguigu.util;
import java.util.Arrays;
public class LogUtils {
public static void logBefore(String method, Object... args) {
System.out.println(" 当前运算是 " + method + " , 参数是: " + Arrays.asList(args));
}
public static void logAfterReturning(String method, Object result) {
System.out.println(" 当前运算是 " + method + " , 结果是: " + result);
}
public static void logAfterThrowing(String method, Exception e) {
System.out.println(" 当前运算是 " + method + " , 抛的异常是: " + e);
}
}
test :
package com.atguigu.test;
import com.atguigu.service.Calculation;
import com.atguigu.service.Impl.Calculator;
public class ProxyTest {
public static void main(String[] args) {
Calculation calculation = new Calculator();
// calculation.add(1 , 1);
calculation.div(1,0);
}
}
9.3 使用代理实现日记
9.3.1 使用jdk动态代理统一日记
必须要有接口
package com.atguigu.service;
public interface Calculation {
int add(int num1, int num2);
int add(int num1, int num2, int num3);
int div(int num1, int num2);
}
接口实现类 :
package com.atguigu.service.Impl;
import com.atguigu.service.Calculation;
import com.atguigu.util.LogUtils;
public class Calculator implements Calculation {
@Override
public int add(int num1, int num2) {
int result = 0;
result = num1 + num2;
return result;
}
@Override
public int add(int num1, int num2, int num3) {
int result = 0;
result = num1 + num2 + num3;
return result;
}
@Override
public int div(int num1, int num2) {
int result = 0;
result = num1 / num2;
return result;
}
}
jdkProxy
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class JdkProxy {
public static Object createProxy(Object object){
/**
* jdk动态代理:
* 租房子:
* 租客======>>目标
* 中介======>>代理
* 租房完毕:=======整个过程:代理过程
* 房东
* 要有接口
* ClassLoader loader, 类加载器
* Class<?>[] interfaces, 所有目标和代理对象的实现类接口集合
* InvocationHandler h 代理增强接口
*
*/
return Proxy.newProxyInstance(
object.getClass().getClassLoader(),
object.getClass().getInterfaces(),
/**
* @param proxy 第一个参数,是代理对象实例
* @param method 第二个参数,它是调用方法的反射对象
* @param args 第三个参数,调用代理方法时传递进来的实参
* @return @return InvocationHandler接口的 invoke() 方法的返回值,就是代理对象方法的返回值.
*/
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object o = null;
try {
// 做一些增强操作
LogUtils.logBefore(method.getName(),args);
// 目标方法
o = method.invoke(object, args);
LogUtils.logAfterReturning(method.getName(),o);
} catch (Exception e) {
e.printStackTrace();
LogUtils.logAfterThrowing(method.getName(),e);
}
return o;
}
}
);
}
}
test:
public class Test {
public static void main(String[] args) {
Calculation calculation = new Calculator();
// calculation.add(1,2);
// calculation.div(1,0);
Calculation calculationProxy = (Calculation) JdkProxy.createProxy(calculation);
calculationProxy.div(1 ,0);
}
}
优点:这种方式已经解决我们前面所有日记需要的问题。非常的灵活(将公用的业务逻辑抽去出来进行重用)。而且可以方便的在后期进行维护和升级。
缺点:当然使用jdk动态代理,需要有接口。如果没有接口。就无法使用jdk动态代理,cglib代理。