1、Spring 优点
- Spring 是一个开源的免费框架(容器)
- Spring 是一个轻量级的、非入侵式的框架
- 控制反转(IOC),面向切面编程(AOP)
- 支持事务处理,对框架整合的支持
总结一句话:Spring 就是一个轻量级的控制反转(IoC)和面向切面编程(Aop)的框架!
2、Spring 组成
3、Ioc 控制反转
- 之前,程序是主动创建对象!控制权在程序员手上
- 通过 set 动态注入,将控制权给用户,即
控制反转(Ioc) - 牵一发而动全身
Ioc 和之前最大的区别就是,将控制权交给用户,而不是程序员
public class UserServiceImpl implements UserService {
private UserDao userDao;
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
public void getUser() {
userDao.getUser();
}
}
4、Spring 入门案例
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"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="UserDaoImpl" class="com.lyq.dao.UserDaoImpl"></bean>
<bean id="UserMysqlImpl" class="com.lyq.dao.UserMysqlImpl"></bean>
<bean id="UserOracleImpl" class="com.lyq.dao.UserOracleImpl"></bean>
<bean id="UserServiceImpl" class="com.lyq.service.Impl.UserServiceImpl">
<property name="userDao" ref="UserOracleImpl"/>
</bean>
</beans>
测试类
public static void main(String[] args) {
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("application.xml");
UserService userServiceImpl = (UserService) applicationContext.getBean("UserServiceImpl");
userServiceImpl.getUser();
}
5、IOC 创建对象的方式
- 使用无参构造器创建对象
(默认) - 使用有参构造器创建对象
- 1. 通过下标
<!-- 第一种,通过构造器的下标方式 --> <bean id="User" class="com.lyq.pojo.User"> <constructor-arg index="0" value="xxxx"/> </bean>- 2. 通过类型
<!-- 第二种,通过构造器,里面的参数类型传递,不建议使用 --> <bean id="User" class="com.lyq.pojo.User"> <constructor-arg type="java.lang.String" value="dasdas"/> </bean>- 3. 通过参数名
<!-- 第三种,直接通过参数名传递 --> <bean id="User" class="com.lyq.pojo.User"> <constructor-arg name="name" value="aaa"/> </bean>
总结:在配置文件加载时,容器中管理的对象就已经初始化了
6、Spring 配置
alias
<alias name="user" alias="user2"/>
Bean 的配置
<bean id="user" class="com.lyq.pojo.User" name="user3,user4">
<property name="name" value="zxc"/>
</bean>
import
一般用于团队开发使用,他开源将多个配置文件,导入合并为一个
在application.xml里面导入其他开发者写的配置文件
<import resource="application2.xml"/>
<import resource="application3.xml"/>
<import resource="application4.xml"/>
7、依赖注入(DI)
7.1 构造器注入
前面已经说过
7.2 Set 方式注入【重点】
- 依赖注入:Set 注入
- 依赖:bean 对象的创建依赖于容器
- 注入:bean 对象中的所有属性,由容器来注入!
【环境搭建】
1. 复杂类型
public class Address {
private String address;
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
}
2. 真实测试对象
public class Student {
private String name;
private Address address;
private String[] books;
private List<String> hobbys;
private Map<String, String> card;
private Set<String> games;
private String wife;
private Properties info;
}
3. application.xml
<bean id="address" class="com.lyq.pojo.Address"/>
<bean id="student" class="com.lyq.pojo.Student">
<!-- 普通类型注入-->
<property name="name" value="李永奇"/>
<!-- bean 注入-->
<property name="address" ref="address"/>
<!-- 数组注入-->
<property name="books">
<array>
<value>红楼梦</value>
<value>西游记</value>
<value>大话西游</value>
</array>
</property>
<!-- list 注入-->
<property name="hobbys">
<list>
<value>听歌</value>
<value>看电影</value>
</list>
</property>
<!-- map 注入-->
<property name="card">
<map>
<entry key="no1" value="我是第一"/>
<entry key="no2" value="我是第二"/>
<entry key="no3" value="我是第三"/>
</map>
</property>
<!-- set 注入-->
<property name="games">
<set>
<value>1</value>
<value>3</value>
<value>3</value>
</set>
</property>
<!-- properties 注入-->
<property name="info">
<props>
<prop key="1">dasd</prop>
</props>
</property>
<!-- null -->
<property name="wife">
<null></null>
</property>
</bean>
4. 测试类
public class MyTest {
public static void main(String[] args) {
ApplicationContext ctx = new ClassPathXmlApplicationContext("application.xml");
Student student = (Student) ctx.getBean("student");
System.out.println(student.getAddress());
}
}
7.3 拓展方式注入
我们可以使用 p 命名空间和 c 命名空间进行注入
<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
https://www.springframework.org/schema/beans/spring-beans.xsd">
<!-- p 命名空间 -->
<bean id="user" class="com.lyq.pojo.User" p:name="user2" p:age="18"/>
<!-- c 命名空间 -->
<bean id="user2" class="com.lyq.pojo.User" c:age="19" c:name="校招"/>
</beans>
测试:
@Test
public void t(){
ApplicationContext ctx = new ClassPathXmlApplicationContext("beans.xml");
User user = ctx.getBean("user", User.class);
System.out.println(user);
}
@Test
public void t2(){
ApplicationContext ctx = new ClassPathXmlApplicationContext("beans.xml");
User user = ctx.getBean("user2", User.class);
System.out.println(user);
}
注意:p 命名空间和 c 命名空间不能直接使用,需要添加约束
xmlns:p="http://www.springframework.org/schema/p"
xmlns:c="http://www.springframework.org/schema/c"
7.4 bean 的作用域
1. 单例模式
(Spring 默认机制)
<bean id="user2" class="com.lyq.pojo.User" c:age="19" c:name="校招"
scope="singleton"/>
2. 原型模式:每次从容器中 get 的时候,都会产生一个新对象!
<bean id="user2" class="com.lyq.pojo.User" c:age="19" c:name="校招"
scope="prototype"/>
3. 其余的 request、session、application 这些只能在 web 开发中使用
8、Bean 的自动装配
- 自动装配是 Spring 满足 bean 依赖的一种方式
- Spring 会在上下文中自动寻找,并自动给 bean 装配属性
在Spring 中有三种装配的的方式
- 在xml中显示的配置
- 在 java 中显示配置
- 隐式的自动装配 bean
【重要】
8.1 测试
环境搭建:一个人有两个宠物
8.2 ByName 自动装配
<!--
byName :会自动在容器上下文中查找,和自己对象 set 方法后面的值对应的 beanid!
因为设置了 setDog 和 setCat ,byName 会自动在 xml 寻找
-->
<bean id="people" class="com.lyq.pojo.People" autowire="byName">
<property name="name" value="李永奇"/>
</bean>
8.3 ByType 自动装配
<!--
byName :会自动在容器上下文中查找,和自己对象 set 方法后面的值对应的 beanid!
byType :会自动在容器上下文查找,和自己对象属性类型相同的
-->
<bean id="people" class="com.lyq.pojo.People" autowire="byType">
<property name="name" value="李永奇"/>
</bean>
小结:
- byName 的时候,需要保证所有 bean 的 id 唯一,并且这个 bean 需要和自动注入的属性的 set 方法值一致
- byType 的时候,需要保证所有 bean 的 class 唯一,并且这个 bean 需要和自动注入的属性的类型一致
8.4 使用注解实现自动装配
1. 导入约束 : context 约束
xmlns:context="http://www.springframework.org/schema/context"
2. 配置注解的支持:context:annotation-config/
<?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
https://www.springframework.org/schema/beans/spring-beans.xsd">
<context:annotation-config></context:annotation-config>
</beans>
@Autowired
- 直接在属性上使用即可!也可以在 set 方法上使用!
- 使用
@Autuwired可以省略写 set 方法,前提是需要自动装配的属性在IOC(Spring)容器中存在,且符合名字 byName !
科普:
@Nullable 字段标记了这个注解,说明这个字段可以为 null
测试代码:
public class People {
@Autowired(required = false)
private Cat cat;
@Autowired
private Dog dog;
private String name;
}
注意: 如果 @Autuwired自动装配的环境比较复杂,自动装配无法通过一个注解【@Autuwired】完成时,我们可以使用@Qualifier(value="xxx")去配置 @Autuwired 的使用,指定注入的 bean 对象
@Resource注解
public class People {
@Resource(name = "cat")
private Cat cat;
}
小结:
- 都是用来自动装配的,都可以放在属性字段上
- @Autowired 通过 byType 的方式实现
- @Resource 默认通过 byName 的方式实现,如果找不到名字,则通过 byType 实现
- 执行顺序不同:
9、使用注解开发
在 spring4 之后,必须要导入 aop 的包
<?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
https://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.lyq.dao"/>
</beans>
1. bean 2. 属性如何注入
@Component // 等价于在 xml 文件中写 <bean id="user">
public class User {
// 等价于 <property name="name" value="lyq" />
@Value("lyq ")
public String name;
}
3. 衍生注解
@Component 有几个衍生注解,我们在 web 开发中,按照 mvc 三层架构分层
- dao 【@Repository】
- service 【@Service】
- controller 【@Controller】
这四个注解功能一样,都是将某个类放到 spring 中,进行 bean 装配
4. 自动装配
@Autowired :自动装配通过类型,名字
如果 Autowired 不能唯一自动装配上属性,则需要通过 @Qualifier(value="xxx")
@Nullable :字段标记了这个注解,表示可以为null
@Resource 自动装配通过名字,类型
5. 作用域(@Scope)
@Component
@Scope("property")
public class User {
// 等价于 <property name="name" value="lyq" />
@Value("lyq ")
public String name;
}
6. 小结
xml 与 注解:
- xml更加万能,使用任何场合
- 注解不是自己类使用不了,维护复杂
xml 与 注解最佳实践:
- 注解适用于属性注入
- xml 用来管理 bean
- 我们在使用过程中,只需要注意一个问题:必须让注解生效
(先在xml扫描),开启注解支持
10、使用 java 方式配置 spring
lyqConfig.class 配置类
@Configuration
@ComponentScan("com.lyq.pojo")
@Import(UserConfig.class)
public class lyqConfig {
@Value("lyq")
private String name;
public String getName() {
return name;
}
@Bean
public User getUser(){
return new User();
}
}
Test.class测试类
public static void main(String[] args) {
ApplicationContext ctx =
new AnnotationConfigApplicationContext(lyqConfig.class);
User getUser = ctx.getBean("getUser", User.class);
System.out.println(getUser.name);
}
11. 代理模式
为什么要学习代理模式? 因为这个是 springAop 和 springmvc 的底层
代理模式的分类:
- 动态代理
- 静态代理
11.1 静态代理
角色分析:
- 抽象角色:一般使用接口或者抽象类来解决
- 真实角色:被代理的角色
- 代理角色:代理真实角色,代理真实角色会,一般会做一些附魔操作
- 客户:访问代理对象的人
代码步骤: 1. 接口
// 租房
public interface Rent {
public void rent();
}
2. 真实角色
// 房东
public class Host implements Rent{
public void rent() {
System.out.println("房东出租");
}
}
3. 代理角色
public class Proxy implements Rent{
private Host host;
public Proxy() {
}
public Proxy(Host host) {
this.host = host;
}
public void rent() {
seeHouse();
host.rent();
hetong();
fare();
}
public void seeHouse(){
System.out.println("中介带你看房");
}
public void fare(){
System.out.println("中介费");
}
public void hetong(){
System.out.println("签合同");
}
}
4. 客户端访问代理角色
public class Client {
public static void main(String[] args) {
Host host = new Host();
Proxy proxy = new Proxy(host);
proxy.rent();
}
}
代理模式的好处:
- 可以使真实角色的操作更加纯粹!不用关注公共业务
- 公共业务就交给代理角色!实现业务分工
- 公共业务发生拓展的时候,方便集中管理
代理模式缺点:
- 一个真实角色就会产生一个代理角色;代码量会翻倍
11.2 静态代理案例(给所有方法添加日志功能)
userService 接口
public interface UserService {
void add();
void delete();
void update();
void query();
}
UserServiceImpl 实现类
public class UserServiceImpl implements UserService{
public void add() {
System.out.println("增加");
}
public void delete() {
System.out.println("删除");
}
public void update() {
System.out.println("修改");
}
public void query() {
System.out.println("查询");
}
}
UserServiceProxy 代理类
public class UserServiceProxy implements UserService {
private UserServiceImpl userService;
public void setUserService(UserServiceImpl userService) {
this.userService = userService;
}
public void add() {
log("add");
userService.add();
}
public void delete() {
log("delete");
userService.add();
}
public void update() {
log("update");
userService.add();
}
public void query() {
log("query");
userService.add();
}
// 日志方法
public void log(String msg){
System.out.println("使用了" + msg + "方法");
}
}
11.3 动态代理
public class ProxyClass implements InvocationHandler {
private Object target;
public void setTarget(Object target) {
this.target = target;
}
// 用来执行方法
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
log(method.getName());
Object invoke = method.invoke(target, args);
return invoke;
}
public Object getProxy(){
// public static Object newProxyInstance(ClassLoader loader,
// Class<?>[] interfaces,
// InvocationHandler h)
return Proxy.newProxyInstance(this.getClass().getClassLoader(),
target.getClass().getInterfaces(), this);
}
public void log(String msg){
System.out.println("执行了" + msg + "方法");
}
}
12、AOP
12.1 使用原生 Spring APi 接口实现
application.xml
<aop:config>
<!--切入点,要执行的位置-->
<aop:pointcut id="pointcut" expression="execution(* com.lyq.service.Impl.UserServiceImpl.*(..))"/>
<!--执行环绕-->
<aop:advisor advice-ref="log" pointcut-ref="pointcut"/>
<aop:advisor advice-ref="afterlog" pointcut-ref="pointcut"/>
</aop:config>
log类
public class AfterLog implements AfterReturningAdvice {
public void afterReturning(Object o, Method method, Object[] objects, Object o1) throws Throwable {
System.out.println(method.getName() + " " + o );
}
}
afterLog类
public class log implements MethodBeforeAdvice {
public void before(Method method, Object[] args, Object target) throws Throwable {
System.out.println(target.getClass().getName() + " " + method.getName());
}
}
12.2 自定义 aop 类实现
application.xml
<bean id="diy" class="com.lyq.diy.DiyPointCut"/>
<aopL:config>
<!-- 自定义切面 -->
<aop:aspect ref="diy">
<!-- 切入点 -->
<aop:pointcut id="point" expression="execution(* com.lyq.service.*.*(..))"/>
<!-- 通知 -->
<aop:before method="before" pointcut-ref="point"/>
<aop:after method="after" pointcut-ref="point"/>
</aop:aspect>
</aopL:config>
DiyPointCut类
public class DiyPointCut {
public void before(){
System.out.println("===========方法执行前=========");
}
public void after() {
System.out.println("==========方法执行后========");
}
}
12.3 使用注解实现
<bean id="annotationPointCut" class="com.lyq.diy.AnnocationPointCunt"/>
<aop:aspectj-autoproxy/>
@Aspect
public class AnnocationPointCunt {
@Before("execution(* com.lyq.service.*.*(..))")
public void before(){
System.out.println("方法执行前");
}
@After("execution(* com.lyq.service.*.*(..))")
public void after(){
System.out.println("方法执行后");
}
@Around("execution(* com.lyq.service.*.*(..))")
public void aroud(ProceedingJoinPoint jp) throws Throwable {
System.out.println("环绕前");
Object proceed = jp.proceed();
System.out.println("环绕后");
System.out.println(jp.getSignature());
}
}