Spring5 入门
1 Spring5
1.1 简介
- Spring:春天--->给软件行业带来了春天!
Spring框架是由于软件开发的复杂性而创建的。Spring使用的是基本的JavaBean来完成以前只可能由EJB完成的事情。然而,Spring的用途不仅仅限于服务器端的开发。从简单性、可测试性和松耦合性角度而言,绝大部分Java应用都可以从Spring中受益。
Spring是一个开源框架,它由[Rod Johnson](https://baike.baidu.com/item/Rod Johnson?fromModule=lemma_inlink)创建。它是为了解决企业应用开发的复杂性而创建的。Spring使用基本的JavaBean来完成以前只可能由EJB完成的事情。然而,Spring的用途不仅限于服务器端的开发。从简单性、可测试性和松耦合的角度而言,任何Java应用都可以从Spring中受益。
Spring是一个轻量级的控制反转(IoC)和面向切面(AOP)的容器框架。
轻量——从大小与开销两方面而言Spring都是轻量的。完整的Spring框架可以在一个大小只有1MB多的JAR文件里发布。并且Spring所需的处理开销也是微不足道的。此外,Spring是非侵入式的:典型地,Spring应用中的对象不依赖于Spring的特定类。
控制反转——Spring通过一种称作控制反转(IoC)的技术促进了松耦合。当应用了IoC,一个对象依赖的其它对象会通过被动的方式传递进来,而不是这个对象自己创建或者查找依赖对象。你可以认为IoC与JNDI相反——不是对象从容器中查找依赖,而是容器在对象初始化时不等对象请求就主动将依赖传递给它。
面向切面——Spring提供了面向切面编程的丰富支持,允许通过分离应用的业务逻辑与系统级服务(例如审计(auditing)和事务(transaction)管理)进行内聚性的开发。应用对象只实现它们应该做的——完成业务逻辑——仅此而已。它们并不负责(甚至是意识)其它的系统级关注点,例如日志或事务支持。
容器——Spring包含并管理应用对象的配置和生命周期,在这个意义上它是一种容器,你可以配置你的每个bean如何被创建——基于一个可配置原型(prototype),你的bean可以创建一个单独的实例或者每次需要时都生成一个新的实例——以及它们是如何相互关联的。然而,Spring不应该被混同于传统的重量级的EJB容器,它们经常是庞大与笨重的,难以使用。
框架——Spring可以将简单的组件配置、组合成为复杂的应用。在Spring中,应用对象被声明式地组合,典型地是在一个XML文件里。Spring也提供了很多基础功能(事务管理、持久化框架集成等等),将应用逻辑的开发留给了你。
所有Spring的这些特征使你能够编写更干净、更可管理、并且更易于测试的代码。它们也为Spring中的各种模块提供了基础支持。
历史
- 2002,首次推出了Spring框架得雏形,interface21框架
- Spring框架即以interface21框架为基础,经过重新设计,并不断丰富其内涵,于2004年3月24日,发布了1.0正式版。
- Rod Johnson Spring架构得创始人
- Spring理念 是现有得技术更加容易使用,本身是个大杂烩,整合了现有得技术框架!
- SSH : Struct2 + Spring + Hibernate!
- SSM : SpringMvc + Spring + Mybatis!
官方下载地址: http://repo.spring.io/release/org/springframework/spring
Github : GitHub - spring-projects/spring-framework: Spring Framework
maven依赖包的地址
https://mvnrepository.com/tags/spring
选择 Spring Web MVC
<!-- https://mvnrepository.com/artifact/org.springframework/spring-webmvc -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>6.0.2</version>
</dependency>
再选一个Spring JDBC
<!-- https://mvnrepository.com/artifact/org.springframework/spring-jdbc -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>6.0.2</version>
</dependency>
1.2 优点
- Spring是一个 开源的免费的框架(容器)!
- Spring是一个轻量级的,非入侵式的框架!
- 控制反转(IOC),面向切面变成(AOP)!
- 支持事务的处理,对框架整合的支持!
总结一句话:Spring就是一个轻量级的控制反转(IOC)和面向切面编程的框架!
1.3 七大模块
1.4 扩展
- Spring boot
- 一个快速开发的脚手架
- 基于SpringBoot可以快速开发单个微服务
- 约定大于配置!
- SpringCloud
- Spring Cloud是基于SpringBoot实现的
因为现在大多数公司都在使用SpringBoot进行快速开发,学习SpringBoot的前提,需要完全掌握Spring以及SpringMVC!
弊端
发展太久了之后,违背了原来的理念!配置十分繁琐,人称:“配置地狱”
2 IOC理论推导
写程序的方式
- UserDao接口
- UserDaoImpl实现类
- UserService业务接口
- UserServiceImpl业务实现类
示例
创建一个maven项目
导入jar包
在pom.xml中添加
<dependencies>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-webmvc -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>6.0.2</version>
</dependency>
</dependencies>
删除项目中的src
创建一个module
选到这层目录
在java中创建一个package,com.goldtree.dao,然后创建一个接口Userdao
设置一个方法
package com.goldtree.dao;
public interface Userdao {
void getUser(); //设置一个方法
}
设置一个实现类
.png)
package com.goldtree.dao;
public class UserDaoImp implements UserDao {
public void getUser() {
System.out.println("默认获取用户的数据");
}
}
创建package com.godtree.service,创建一个接口UserService
package com.goldtree.service;
public interface UserService {
void getUser();
}
创建UserServiceImpl实现类
package com.goldtree.service;
import com.goldtree.dao.UserDaoImpl;
import com.goldtree.dao.UserDao;
public class UserServiceImpl implements UserService{
private UserDao userDao = new UserDaoImpl();
public void getUser() {
userDao.getUser();
}
}
创建一个测试类
import com.goldtree.service.UserService;
import com.goldtree.service.UserServiceImpl;
public class Mytest {
public static void main(String[] args) {
//用户实际调用的是业务层,dao层用户不需要接触
UserService userService = new UserServiceImpl();
userService.getUser();
}
}
文件结构
客户需求变了
添加了一个新的dao类
package com.goldtree.dao;
public class UserDaoMysqlImpl implements UserDao{
public void getUser() {
System.out.println("Mysql获取用户数据");
}
}
客户想调用UserDaoMysqlImpl
这就要修改UserServiceImpl实现类,才能实现。
import com.goldtree.dao.UserDaoImpl;
import com.goldtree.dao.UserDao;
import com.goldtree.dao.UserDaoMysqlImpl;
public class UserServiceImpl implements UserService{
private UserDao userDao = new UserDaoMysqlImpl();
public void getUser() {
userDao.getUser();
}
}
我们想想看,如果有100个新的需求,我们就要修改100次,实现类,没法活了啊。
怎么办呢?
这么办
使用set进行动态实现值的注入
package com.goldtree.service;
import com.goldtree.dao.UserDao;
import com.goldtree.dao.UserDaoSqlserverImpl;
public class UserServiceImpl implements UserService{
private UserDao userDao;
//利用set进行动态实现值的注入!
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
public void getUser() {
userDao.getUser();
}
}
import com.goldtree.dao.UserDao;
import com.goldtree.dao.UserDaoImpl;
import com.goldtree.dao.UserDaoSqlserverImpl;
import com.goldtree.service.UserService;
import com.goldtree.service.UserServiceImpl;
public class Mytest {
public static void main(String[] args) {
//用户实际调用的是业务层,dao层用户不需要接触
UserService userService = new UserServiceImpl();
((UserServiceImpl) userService).setUserDao(new UserDaoSqlserverImpl());
userService.getUser();
}
}
在之前的业务中,用户的需求可能会影响我们原来的代码,我们需要根据用户的需求去修改源代码。如果程序代码量十分大,修改一次的成本是很大的
我们使用一个接口实现,已经发生了革命性的变化
private UserDao userDao;
//利用set进行动态实现值的注入!
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
- 之前,程序是主动创建对象!控制权在程序员手上!
- 使用了set注入后,程序不再具有主动性,而是变成了被动的接受对象!
这种思想,从本质上解决了问题,我们程序员不用再去管理对象的创建了。系统的耦合性大大降低,可以更加专注在业务的实现上。这是IOC的原型。
HelloSpring
生成一个实体类
package com.goldtree.pojo;
public class Hello {
private String str;
public String getStr() {
return str;
}
public void setStr(String str) {
this.str = str;
}
@Override
public String toString() {
return "Hello{" +
"str='" + str + '\'' +
'}';
}
}
创建一个beans.xml文件
Spring Framework 中文文档 - Core Technologies | Docs4dev
<?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">
<!--使用Spring来创建对象,在Spring这些都称为Bean
类型 变量名 = new 类型();
bean = 对象 new Hello();
id = 变量名
class = new 的对象类型
property相当于给对象中的属性设置一个值
-->
<bean id="hello" class="com.goldtree.pojo.Hello">
<property name="str" value="Spring"/>
</bean>
</beans>
创建一个测试类
import com.goldtree.pojo.Hello;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Mytest01 {
public static void main(String[] args) {
//获取Spring的上下文对象
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
//我们的对象现在都在Spring中管理了,要使用,直接去里面取出来就可以了
Hello hello = (Hello) context.getBean("hello");
System.out.println(hello.toString());
}
}
思考几个问题
-
Hello对象是谁创建的
hello对象是由Spring创建的
-
Hello对象的属性是怎么是设置的
hello对象的属性是由Spring容器设置的
这个过程就叫做控制反转:
控制:谁来控制对象的创建,传统应用程序的对象是由程序本身控制创建的,使用Spring后,对象是由Spring来创建的
反转:程序本身不创建对象,而变成被动的接受对象
依赖注入:就是利用set方法来进行注入的
IOC是一种编程思想,由主动的编程变成被动的接收
使用Spring改造之前的代码
创建一个beans.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="UserDaoImpl" class="com.goldtree.dao.UserDaoImpl"/>
<bean id="UserDaoMysqlImpl" class="com.goldtree.dao.UserDaoMysqlImpl"/>
<bean id="UserDaoSqlserverImpl" class="com.goldtree.dao.UserDaoSqlserverImpl"/>
<bean id="UserServiceImpl" class="com.goldtree.service.UserServiceImpl">
<property name="userDao" ref="UserDaoMysqlImpl"/>
<!--
ref:引用Spring容器中的建立好的对象
value:引用具体的值,基本数据类型
-->
</bean>
</beans>
创建一个测试类
import com.goldtree.service.UserServiceImpl;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Mytest03 {
public static void main(String[] args) {
// 获取ApplicationContext:拿到Spring的容器
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
// 容器在手,天下我有,需要什么,就get什么
UserServiceImpl userServiceImpl = (UserServiceImpl) context.getBean("UserServiceImpl");
userServiceImpl.getUser();
}
}
IOC创建对象的方式
创建一个类
package com.goldtree.pojo;
public class User {
/*public User() {
System.out.println("User的无参构造");
}
*/
public User(String name){
this.name = name;
}
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public void show(){
System.out.println("name: "+ name);
}
}
-
使用无参构造创建对象
<?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="user" class="com.goldtree.pojo.User"> <property name="name" value="SpongeBob"/> </bean> </beans>
-
假设使用有参构造创建对象
-
下标赋值
<?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="user" class="com.goldtree.pojo.User"> <constructor-arg index="0" value="PatrikStar"/> </bean> </beans>
-
通过类型赋值
<?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="user" class="com.goldtree.pojo.User"> <constructor-arg type="java.lang.String" value="goldtree"/> </bean> </beans>
-
通过参数名
<?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="User" class="com.goldtree.pojo.User"> <constructor-arg name="name" value="littleBall"/> </bean> </beans>
总结:在配置文件加载的时候,容器中管理的对象就已经初始化了
-
Spring配置
别名
<?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="user" class="com.goldtree.pojo.User">
<property name="name" value="SpongeBob"/>
</bean>
<alias name="user" alias="hahaha"/>
</beans>
Bean的配置
<?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">
<!--
id : bean的唯一标识符,也就是相当于我们学的对象名
class : bean对象所对应的全限定名 包名 + 类型
name : 也是别名
-->
<bean id="user" class="com.goldtree.pojo.User" name="user02 user03,user04">
<property name="name" value="SpongeBob"/>
</bean>
</beans>
import
这个import,一般用于团队开发,可以把多个beans.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">
<import resource="beans.xml"/>
<import resource="beans01.xml"/>
<import resource="beans02.xml"/>
<import resource="beans03.xml"/>
</beans>
DI依赖注入环境
构造器注入
Set方式注入【重点】
- 依赖注入:Set注入!
- 依赖:bean对象的创建依赖于容器
- 注入:bean对象中的所有属性,由容器来注入
【环境搭建】
-
复杂类型
package com.goldtree.pojo; import org.springframework.aop.target.LazyInitTargetSource; import java.util.*; public class Student { private String name; private Address address; private String[] books; private List<String> hobbies; private Map<String, String> card; private Set<String> games; private String gender; private Properties info; public String getName() { return name; } public void setName(String name) { this.name = name; } public Address getAddress() { return address; } public void setAddress(Address address) { this.address = address; } public String[] getBooks() { return books; } public void setBooks(String[] books) { this.books = books; } public List<String> getHobbies() { return hobbies; } public void setHobbies(List<String> hobbies) { this.hobbies = hobbies; } public Map<String, String> getCard() { return card; } public void setCard(Map<String, String> card) { this.card = card; } public Set<String> getGames() { return games; } public void setGames(Set<String> games) { this.games = games; } public String getGender() { return gender; } public void setGender(String gender) { this.gender = gender; } public Properties getInfo() { return info; } public void setInfo(Properties info) { this.info = info; } @Override public String toString() { return "Student{" + "name='" + name + '\'' + ", address=" + address + ", books=" + Arrays.toString(books) + ", hobbies=" + hobbies + ", card=" + card + ", games=" + games + ", gender='" + gender + '\'' + ", info=" + info + '}'; } }
package com.goldtree.pojo; public class Address { private String address; public String getAddress() { return address; } public void setAddress(String address) { this.address = address; } @Override public String toString() { return "Address{" + "address='" + address + '\'' + '}'; } }
-
真实测试对象
<?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 ="address" class="com.goldtree.pojo.Address"> <property name="address" value="beijing"/> </bean> <bean id = "student" class="com.goldtree.pojo.Student"> <!--第一种,普通值注入--> <property name="name" value="SpongeBob"/> <!--第二种,Bean注入 --> <property name="address" ref="address"/> <!--数组注入 --> <property name="books"> <array> <value>红楼梦</value> <value>西游记</value> <value>水浒传</value> <value>三国演义</value> </array> </property> <!--List注入 --> <property name="hobbies"> <list> <value>打小怪兽</value> <value>撸猫</value> </list> </property> <!--map注入 --> <property name="card"> <map> <entry key="name" value="SpongeBob"/> <entry key="gender" value="male"/> </map> </property> <!--set注入 --> <property name="games"> <set> <value>原神</value> <value>阴阳师</value> </set> </property> <!--空值注入 --> <property name="gender"> <null/> </property> <!--Properties --> <property name="info"> <props> <prop key="学号">000001</prop> <prop key="邮箱">xxx@sss.com</prop> </props> </property> </bean> </beans>
-
测试类
import com.goldtree.pojo.Student; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class Mytest { public static void main(String[] args) { ApplicationContext Context = new ClassPathXmlApplicationContext("beans.xml"); Student student = (Student) Context.getBean("student"); //student.getName(); System.out.println(student.toString()); } }
-
ssss
扩展方式注入
c命名和p命名空间注入
5.3.1 p命名空间注入
-
简单类
package com.goldtree.pojo; public class User { private String name; private int age; public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } @Override public String toString() { return "User{" + "name='" + name + '\'' + ", age=" + age + '}'; } }
-
beans.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:p="http://www.springframework.org/schema/p" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <!--p命名空间注入,可以直接注入属性的值 --> <bean id = "user" class="com.goldtree.pojo.User" p:name="goldtree" p:age="25"/> </beans>
-
测试类
import com.goldtree.pojo.User; import org.junit.Test; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class MyTest01 { public static void main(String[] args) { } @Test public void test2(){ ApplicationContext Context = new ClassPathXmlApplicationContext("User.xml"); User user = (User) Context.getBean("user"); System.out.println(user.toString()); } }
5.3.2 c命名空间注入
-
简单类
package com.goldtree.pojo; public class User { private String name; private int age; public User() { } public User(String name, int age) { this.name = name; this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } @Override public String toString() { return "User{" + "name='" + name + '\'' + ", age=" + age + '}'; } }
-
beans.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:p="http://www.springframework.org/schema/p" xmlns:c="http://www.springframework.org/schema/c" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <!--p命名空间注入,可以直接注入属性的值,通过属性 --> <bean id = "user" class="com.goldtree.pojo.User" p:name="goldtree" p:age="25"/> <!--c命名空间注入,通过有参构造器注入值 --> <bean id = "user2" class="com.goldtree.pojo.User" c:name="SpongeBob" c:age="33"/> </beans>
-
测试类
import com.goldtree.pojo.User; import org.junit.Test; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class MyTest01 { public static void main(String[] args) { } @Test public void test2(){ ApplicationContext Context = new ClassPathXmlApplicationContext("User.xml"); User user = Context.getBean("user2",User.class); System.out.println(user.toString()); User user2 = Context.getBean("user",User.class); User user3 = Context.getBean("user",User.class); System.out.println(user3 == user2); } }
5.4 bean的作用域
作用域
Scope | Description |
---|---|
singleton | (默认)将每个 Spring IoC 容器的单个 bean 定义范围限定为单个对象实例。 |
prototype | 将单个 bean 定义的作用域限定为任意数量的对象实例 |
request | 将单个 bean 定义的范围限定为单个 HTTP 请求的生命周期。也就是说,每个 HTTP 请求都有一个在单个 bean 定义后面创建的 bean 实例。仅在可感知网络的 Spring ApplicationContext 中有效。 |
session | 将单个 bean 定义的范围限定为 HTTP Session 的生命周期。仅在可感知网络的 Spring ApplicationContext 上下文中有效。 |
application | 将单个 bean 定义的范围限定为ServletContext 的生命周期。仅在可感知网络的 Spring ApplicationContext 上下文中有效 |
websocket | 将单个 bean 定义的范围限定为WebSocket 的生命周期。仅在可感知网络的 Spring ApplicationContext 上下文中有效 |
-
单利模式(默认)
<bean id = "user" class="com.goldtree.pojo.User" p:name="goldtree" p:age="25" scope="singleton"/>
-
原型模式:每次从容器get的时候,都会产生一个新对象
<bean id = "user" class="com.goldtree.pojo.User" p:name="goldtree" p:age="25" scope="prototype"/>
-
其余在web开发中使用
6 自动装配bean(Autowired)
- 自动装配是Spring满足bean依赖一种方式!
- Spring会在上下文中自动寻找,并自动给bean装配属性!
在Spring中有三种装配的方式
- 在xml中显式的配置
- 在java中显式配置
- 隐藏式的自动装配bean【重要】
6.1 测试
-
环境搭建 : 一个人带了两个宠物
package com.goldtree.pojo; public class Cat { public void shout(){ System.out.println("喵喵喵 喵喵喵"); } }
package com.goldtree.pojo; public class Dog { public void shout(){ System.out.println("汪汪汪 汪汪汪"); } }
package com.goldtree.pojo; public class Person { private Cat cat; private Dog dog; private String name; public Cat getCat() { return cat; } public void setCat(Cat cat) { this.cat = cat; } public Dog getDog() { return dog; } public void setDog(Dog dog) { this.dog = dog; } public String getName() { return name; } public void setName(String name) { this.name = name; } @Override public String toString() { return "Person{" + "cat=" + cat + ", dog=" + dog + ", name='" + name + '\'' + '}'; } }
<?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 = "cat" class="com.goldtree.pojo.Cat"/> <bean id = "dog" class="com.goldtree.pojo.Dog"/> <bean id = "person" class="com.goldtree.pojo.Person"> <property name="name" value="小丸子"/> <property name="cat" ref="cat"/> <property name="dog" ref="dog"/> </bean> </beans>
import com.goldtree.pojo.Person; import org.junit.Test; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class MyTest01 { @Test public void Test(){ ApplicationContext Context = new ClassPathXmlApplicationContext("beans.xml"); Person person = Context.getBean("person", Person.class); System.out.println(person.toString()); person.getCat().shout(); person.getDog().shout(); } }
6.2自动装配
6.2.1 Byname自动装配
<!--
Byname : 会自动在容器上下文中查找,和自己对象set方法后面的值对应的beanid!
-->
<bean id = "person" class="com.goldtree.pojo.Person" autowire="byName">
<property name="name" value="小丸子"/>
</bean>
6.2.2 Bytype自动装配
Bytype : 会自动在容器上下文中查找,和自己对象属性类型相同的beanid!
-->
<bean id = "person" class="com.goldtree.pojo.Person" autowire="byType">
<property name="name" value="小丸子"/>
</bean>
小结
- Byname的时候,需要所有bean的id唯一,并且id需要与自动注入的属性的set方法的值一致
- Bytype的时候,需要所有bean的class唯一,需要与自动注入的属性对象的类型一致
6.3 使用注释实现自动装配
jdk1.5支持的注解,Spring2.5支持的注解
要使用注解须知:
-
导入约束:context约束
-
配置注解的支持
<?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:annotation-config/> </beans>
6.3.1 @Autowired
直接在属性上使用即可,也可以在set方式上使用
<?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"
xmlns:aop="http://www.springframework.org/schema/aop"
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
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<!--开启注解的支持 -->
<context:annotation-config/>
<bean id = "cat" class="com.goldtree.pojo.Cat"/>
<bean id = "dog" class="com.goldtree.pojo.Dog"/>
<bean id = "person" class="com.goldtree.pojo.Person"/>
</beans>
@Autowired是通过反射实现的,类中的set方法也可以省略,前提是这个自动装配的属性在IOC(Spring)容器中存在,且先要符合Byxxxx规则(先检查Bytype,然后Byname)
也可以使用@Resource
package com.goldtree.pojo;
import org.springframework.beans.factory.annotation.Autowired;
public class Person {
@Autowired
private Cat cat;
@Autowired
private Dog dog;
private String name;
public Cat getCat() {
return cat;
}
public Dog getDog() {
return dog;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Person{" +
"cat=" + cat +
", dog=" + dog +
", name='" + name + '\'' +
'}';
}
}
如果@Autowired自动装配的环境比较复杂,自动装配无法通过一个注解【@Autowired】完成的时候,可以使用@Qualifier(value = "cat11")去配合@Autowired使用,指定一个唯一的bean对象注入
@Autowired
@Qualifier(value = "cat11")
private Cat cat;
<bean id = "cat1" class="com.goldtree.pojo.Cat"/>
<bean id = "cat11" class="com.goldtree.pojo.Cat"/>
<bean id = "dog1" class="com.goldtree.pojo.Dog"/>
<bean id = "dog11" class="com.goldtree.pojo.Dog"/>
<bean id = "person" class="com.goldtree.pojo.Person"/>
7 Spring注解开发
准备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"
xmlns:aop="http://www.springframework.org/schema/aop"
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
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<!--指定要扫描得包,这个包下得注解就会生效 -->
<context:component-scan base-package="com.goldtree.pojo"/>
<!--开启注解的支持 -->
<context:annotation-config/>
</beans>
准备User类文件
package com.goldtree.pojo;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
//等价于 <bean id = "user" class="com.goldtree.pojo.User"/>
//@Component 组件
@Component
@Scope("singleton")
public class User {
/*等价于
<bean id = "user" class="com.goldtree.pojo.User">
<property name="name" value="小丸子真可爱"/>
</bean>
*/
//@Value("小丸子真可爱")
public String name;
@Value("小丸子真可爱")
public void setName(String name) {
this.name = name;
}
}
准备测试类文件
import com.goldtree.pojo.User;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class MyTest01 {
@Test
public void test(){
ApplicationContext Context = new ClassPathXmlApplicationContext("applicationContext.xml");
User user = Context.getBean("user",User.class);
System.out.println(user.name);
}
}
7.1 注解说明
- @Autowired:通过类型,名字自动装配
- @Component:组件,放在类上,说明这个类被Spring管理了。就相当于
了一下 - @Value:可以直接给属性赋值
- @Scope("singleton") :作用域
7.2 衍生注解
@Component有几个衍生注解,我们在web开发中,会按照mvc三层架构分层!
- dao 【@Repository】跟@Component功能一样
- service 【@Service】
- controller 【@Controller】
这四个注解功能都是一样的,都是代表将某个类注册到Spring容器中,装配Bean
7.3 小结
xml与注解比较
- xml万能,适用于任何场合,维护简单方便
- 注解 不是自己类使用不了,维护相对复杂
xml与注解的最佳实践
-
xml用来管理bean
-
注解只负责完成属性的注入
-
在使用注解的过程中,需要开启注解的支持
<!--指定要扫描得包,这个包下得注解就会生效 --> <context:component-scan base-package="com.goldtree"/> <!--开启注解的支持 --> <context:annotation-config/>
8 使用JavaConfig实现配置
现在不使用Spring的xml配置了,全交给Java来处理
JavaConfig是Spring的一个子项目,在Spring4之后,它成为了一个核心功能!
User类
package com.goldtree.pojo;
import org.springframework.beans.factory.annotation.Value;
public class User {
private String name;
public String getName() {
return name;
}
@Value("可爱小丸子")
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
'}';
}
}
config配置类
package com.goldtree.config;
import com.goldtree.pojo.User;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
//这个也会被Spring容器托管,注册到容器中
//@Configuration代表这是一个配置类,相当于beans.xml
@Configuration
@ComponentScan("com.goldtree.pojo")
@Import(MyConfig02.class)//导入其他的config配置
public class MyConfig {
@Bean//注册一个bean <bean id="User" class="com.goldtree.pojo.User"/>
public User User(){
return new User();
}
}
测试类
import com.goldtree.config.MyConfig;
import com.goldtree.pojo.User;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class MyTest {
public static void main(String[] args) {
ApplicationContext Context = new AnnotationConfigApplicationContext(MyConfig.class);
User user = (User) Context.getBean("User");
System.out.println(user.getName());
}
}
9 代理模式
为什么要学习代理模式?因为这就是SpringAOP的底层!【SpringAOP和SpringMVC】
代理模式的分类:
- 静态代理
- 动态代理
9.1 静态代理
角色分析
- 抽象角色:一般会使用接口或者抽象类解决
- 真实角色:被代理的角色
- 代理角色:代理真实角色,代理真实角色后,一般会做一些附属操作
- 客户:访问代理对象的人
代码步骤
-
接口
package com.goldtree.Demo01; //租房 public interface Renting { public void rent(); }
-
真实角色
package com.goldtree.Demo01; //房东 public class LandLord implements Renting{ public void rent(){ System.out.println("房东要出租房子了"); } }
-
代理角色
package com.goldtree.Demo01; public class Proxy implements Renting{ private LandLord landLord; public Proxy() { } public Proxy(LandLord landLord) { this.landLord = landLord; } @Override public void rent() { landLord.rent(); seeHouse(); showmethemoney(); contract(); } //看房 public void seeHouse(){ System.out.println("中介带你看房"); } //收中介费 public void showmethemoney(){ System.out.println("中介收你中介费"); } public void contract(){ System.out.println("中介带你签合同"); } }
-
客户端访问代理角色
package com.goldtree.Demo01; public class Tenant { public static void main(String[] args) { //房东要租房子 LandLord landLord = new LandLord(); //代理,中介不但帮房东租房子,而且会有一些附属操作 Proxy proxy = new Proxy(landLord); System.out.print("代理说: "); proxy.rent(); } }
代理模式的好处:
- 可以使真实角色的操作更加纯粹,不用去关注一些公共的业务
- 公共业务就交给代理角色,实现了业务的分工
- 公共业务发生扩展的时候,方便集中管理
缺点:
- 一个真实角色就会产生一个代理角色:代码量会翻倍,开发效率会变低
9.2 动态代理
- 动态代理和静态代理角色一样
- 动态代理的代理类是动态生成的,不是我们直接写好的
- 动态代理分为两大类:基于接口的动态代理,基于类的动态代理
- 基于接口:JDK动态代理
- 基于类:cglib
- java字节码实现:javasist
需要了解两个类:
- Proxy :代理
- InvocationHandler :调用处理程序
package com.goldtree.Demo01;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
//会用这个类自动生成代理类
public class ProxyInvocationHandler implements InvocationHandler {
//被代理的接口
private Renting rent;
public void setRent(Renting rent) {
this.rent = rent;
}
//生成得到代理类
//.getClass().getInterfaces() 指定生成的代理有哪些方法
public Object getProxy(){
return Proxy.newProxyInstance(this.getClass().getClassLoader(),rent.getClass().getInterfaces(),this);
}
//处理代理实例,并返回结果
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
seeHouse();
//动态代理的本质,就是使用反射机制实现!
Object result = method.invoke(rent, args);
money();
return result;
}
public void seeHouse(){
System.out.println("中介带看房子");
}
public void money(){
System.out.println("中介收中介费");
}
}
package com.goldtree.Demo01;
public class Client {
public static void main(String[] args) {
//真实角色
LandLord landLord = new LandLord();
//代理角色
ProxyInvocationHandler pih = new ProxyInvocationHandler();
//通过调用程序处理角色来处理我们要调用的接口对象!
pih.setRent(landLord);
//这里的proxy就是动态生成的,我们并没有写
Renting proxy = (Renting) pih.getProxy();
proxy.rent();
}
}
10 反射
10.1 Reflection
-
Reflection(反射)是Java被视为动态语言的关键,反射机制允许程序在执行期借助于Reflection API取得任何类的内部信息,并能直接操作任意对象的内部属性以及方法
Class c = Class.forName("java.lang.String")
-
加载完类之后,在堆内存的方法区中就产生了一个Class类型的对象(一个类只有一个Class对象),这个对象就包含了完整的类的结构信息。我们可以通过这个对象看到类的结构。这个对象就像一面镜子,透过这个镜子看到类的结构,所以,我们形象的称之为:反射
正常方式:引入需要的"包类"名称--->通过new实例化--->取得实例化对象
反射方法:实例化对象--->getClass()方法--->得到完整的"包类"名称
Java 反射机制研究及应用
Java反射机制提供的功能
- 在运行时判断任意一个对象所属的类
- 在运行时构造任意一个类的对象
- 在运行时判断任意一个类所具有的成员变量和方法
- 在运行时获取泛型信息
- 在运行时调用任意一个对象的成员变量和方法
- 在运行时处理注释
- 生成动态代理
Java 放射的优点和缺点
优点:
可以实现动态创建对象和编译,体现出很大的灵活性
缺点:
对性能有影响。使用反射基本上是一种解释操作,我们可以告诉JVM,我们希望做什么并且它满足我们的要求。这类操作总是慢于直接执行相同的操作。
反射相关的主要API
- java.lang.Class:代表一个类
- java.lang.reflect.Method:代表类的方法
- java.lang.reflect.Field:代表类的成员变量
- java.lang.reflect.Constructor:代表类的构造器
代码举例
package com.goldtree;
//什么叫反射
public class Test01 {
public static void main(String[] args) throws ClassNotFoundException {
//通过反射获取类的Class对象
Class c1 = Class.forName("com.goldtree.User");
System.out.println(c1);
Class c2 = Class.forName("com.goldtree.User");
Class c3 = Class.forName("com.goldtree.User");
Class c4 = Class.forName("com.goldtree.User");
//一个类在内存中只有一个Class对象
//一个类被加载后,类的整个结构都会被封装在class对象中
System.out.println(c1.hashCode());
System.out.println(c2.hashCode());
System.out.println(c3.hashCode());
System.out.println(c4.hashCode());
}
}
//实体类:pojo
class User{
private String name;
private int id;
private int age;
public User() {
}
public User(String name, int id, int age) {
this.name = name;
this.id = id;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
", id=" + id +
", age=" + age +
'}';
}
}
10.2 Class类
在Object类中定义了一下的方法,此方法将被所有子类继承
public final Class getClass()
- 以上的方法返回值的类型是一个Class类,此类是Java反射的源头,实际上所谓反射从程序的运行结果来看也很好理解,即:可以通过对象反射求出类的名称。
对象照镜子后可以得到的信息:
- 某个类的属性,方法和构造器
- 某个类到底实现了哪些接口
对于每个类而言,JRE都为其保留一个不变的Class类型的对象。一个Class对象包含了特定某个结构的有关信息
- Class本身也是一个类
- CLass对象只能由系统建立对象
- 一个加载的类在JVM中只会有一个Class实例
- 一个Class对象对应的是一个加载到JVM中的一个.class文件
- 每个类的实例都会记得自己是由哪个Class实例所生成
- 通过Class可以完整的得到一个类中的所有被加载的结构
- Class类是Reflection的根源,针对任何你想动态加载,运行的类,唯有鲜活的相应的Class对象
Class类的常用方法
方法名 | 功能说明 |
---|---|
static ClassforName(String name) | 返回指定类名name的Class对象 |
Object newInstance() | 调用缺省构造函数,返回Class对象的一个实例 |
getName() | 返回此Class对象所表示的实体(类,接口,数组类或void)的名称 |
Class getSuperClass() | 返回当前Class对象的父类的Class对象 |
Class[] getinterfaces() | 获取当前Class对象的接口 |
ClassLoader getClassLoader() | 返回该类的类加载器 |
Constructor[] getConstructors() | 返回一个包含某些Constructor对象的数组 |
Method getMothed(String name,Class.. T) | 返回一个Method对象,此对象的形参类型为paramType |
Field[] getDeclaredFields() | 返回Field对象的一个数组 |
获取Class类的实例
-
若已知具体的类,通过类的class属性获取,该方法最为安全可靠,程序性能最高
Class clazz = Person.class;
-
已知某个类的实例,调用该实例的getClass()方法获取Class对象
Class clazz = person.getClass();
-
已知一个类的全类名,且该类在类路径下,可通过Class类的静态方法forName()获取,可能抛出ClassNotFoundException
Class clazz = Class.forName("demo01.student");
-
内置基本数据类型可以直接用类名.Type
-
还可以利用ClassLoader
代码举例
package com.goldtree;
//测试Class类的创建方式有哪些
public class Test02 {
public static void main(String[] args) throws ClassNotFoundException {
Person person = new Student();
Person person2 = new Student();
System.out.println("这个人是:" + person.name);
//方式一 :通过对象获得
Class c1 = person2.getClass();
System.out.println(c1.hashCode());
//方式二 :forName获得
Class c2 = Class.forName("com.goldtree.Student");
System.out.println(c2.hashCode());
//方式三 : 通过类名.class获得
Class c3 = Student.class;
System.out.println(c3.hashCode());
//方式四 : 基本内置类型的包装类都有一个Type属性
Class type = Integer.TYPE;
System.out.println(type.hashCode());
//获得父类类型
Class c5 = c1.getSuperclass();
System.out.println(c5);
}
}
class Person{
public String name;
public Person() {
}
public Person(String name) {
this.name = name;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
'}';
}
}
class Student extends Person{
public Student(){
this.name="学生";
}
}
class Teacher extends Person{
public Teacher(){
this.name="老师";
}
}
哪些类型可以有Class对象?
- class:外部类,成员(成员内部类,静态内部类),局部内部类,匿名内部类
- interface:接口
- []:数组
- enum:枚举
- annotation:注解
- primitive type:基本数据类型
- void
代码举例
package com.goldtree;
import java.lang.annotation.ElementType;
//所有类型的class
public class Test03 {
public static void main(String[] args) {
Class c1 = Object.class; //类
Class<Comparable> c2 = Comparable.class; //接口
Class<String[]> c3 = String[].class; //数组
Class<int[][]> c4 = int[][].class; //二维数组
Class<Override> c5 = Override.class; //注解
Class<ElementType> c6 = ElementType.class; //enum 枚举
Class<Integer> c7 = Integer.class; //基本类型
Class<Void> c8 = void.class; //void
Class<Class> c9 = Class.class; //class
System.out.println(c1);
System.out.println(c2);
System.out.println(c3);
System.out.println(c4);
System.out.println(c5);
System.out.println(c6);
System.out.println(c7);
System.out.println(c8);
System.out.println(c9);
//只要元素类型与维度一样,就是同一个Class
int[] a = new int[10];
int[][] b = new int[100][100];
System.out.println(a.getClass().hashCode());
System.out.println(b.getClass().hashCode());
}
}
10.3 Java内存分析
了解:类的加载过程
当程序主动使用某个类时,如果该类还未被加载到内存中,则系统会通过如下三个步骤来对该类进行初始化
类的加载与ClassLoader的理解
- 加载:将class文件字节码内容加载到内存中,并将这些静态数据转换成方法区的运行时数据结构,然后生成一个代表这个类的java.lang.Class对象
- 链接:将java类的二进制代码合并到JVM的运行状态之中的过程。
- 验证:确保加载的类信息符合JVM规范,没有安全方面的问题
- 准备:正式为类变量(static)分配内存并设置类变量默认初始值的阶段,这些内存都将在方法区中进行分配。
- 解析:虚拟机常量池内的符号引用(常量名)替换为直接引用(地址)的过程
- 初始化:
- 执行类构造器
()方法的过程。类构造器 ()方法是由编译器自动收集类中所有类变量的赋值动作和静态代码块中的语句合并产生的。(类构造器是构造类信息的,不是构造该类对象的构造器) - 当初始化一个类的时候,如果发现其父类还没有进行初始化,则需要先触发其父类的初始化
- 虚拟机会保证一个类的
()方法在多县城环境中被正确加锁和同步。
- 执行类构造器
测试代码
package com.goldtree;
public class Test04 {
public static void main(String[] args) {
A a = new A();
System.out.println(a.m);
}
}
class A {
static int m = 100;
static {
System.out.println("A类静态代码块初始化");
m = 300;
}
public A() {
System.out.println("A类的无参构造");
}
}
什么时候会发生类初始化?
- 类的主动引用(一定会发生类的初始化)
- 当虚拟机启动,先初始化main方法所在的类
- new一个类的对象
- 调用类的静态成员(除了final常量)和静态方法
- 使用java.lang.reflect包的方法对类进行反射调用
- 当初始化一个类,如果其父类没有被初始化,则先会初始化它的父类
- 类的被动引用(不会发生类的初始化)
- 当访问一个静态域时,只有真正声明这个域的类才会被初始化。如:当通过子类引用父类的静态变量,不会导致子类初始化
- 通过数组定义类引用,不会触发此类的初始化
- 引用常量不会触发此类的初始化(常量在链接阶段就存入调用类的常量池中了)
说明代码
package com.goldtree;
public class Test05 {
static {
System.out.println("main类被加载");
}
public static void main(String[] args) throws ClassNotFoundException {
//1.主动引用
//Son son = new Son();
//反射也会产生主动引用
//Class.forName("com.goldtree.Son");
//不会产生类的引用的方法
System.out.println(Son.b);
}
}
class Father{
static int b =2;
static {
System.out.println("父类被加载");
}
}
class Son extends Father{
static {
System.out.println("子类被加载");
m = 300;
}
static int m =100;
static final int M = 1;
}
10.4 类加载器的作用
- 类加载的作用:将class文件字节码内容加载到内存中,并将这些静态数据转换成方法区的运行时数据结构,然后在堆中生成一个代表这个类的java.lang.Class对象,作为方法区中类数据的访问入口
- 类缓存:标准的JavaSE类加载器可以按要求查找类,但一旦某个类被加载到类加载器中,它将维持加载一段时间。不过JVM垃圾回收机制可以回收这些Class对象。
package com.goldtree;
public class Test06 {
public static void main(String[] args) throws ClassNotFoundException {
//获取系统类的加载器
ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader();
System.out.println(systemClassLoader);
//获取系统类加载器的父类加载器--->扩展类加载器
ClassLoader parent = systemClassLoader.getParent();
System.out.println(parent);
//获取扩展类加载器的父类加载器--->根加载器(c/c++)
ClassLoader parent1 = parent.getParent();
System.out.println(parent1);
//测试当前类是哪个加载器加载的
ClassLoader classLoader = Class.forName("com.goldtree.Test06").getClassLoader();
System.out.println(classLoader);
//测试JDK内置的类是谁加载的
ClassLoader classLoader1 = Class.forName("java.lang.Object").getClassLoader();
System.out.println(classLoader1);
//如何获得系统类加载器可以加载的路径
System.out.println(System.getProperty("java.class.path"));
}
}
10.5 创建运行时类的对象
10.5.1 获取运行时类的完整结构
通过反射获取运行时类的完整结构
Field Method Constructor Superclass Interface Annotation
- 实现的全部接口
- 所继承的父类
- 全部的构造器
- 全部的方法
- 全部的Field
- 注解
package com.goldtree;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
//获得类的信息
public class Test07 {
public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, NoSuchMethodException {
Class c1 = Class.forName("com.goldtree.User");
User user = new User();
c1 = user.getClass();
//获得类的名字
System.out.println(c1.getName()); //获得包名 + 类名
System.out.println(c1.getSimpleName()); //获得类名
//获得类的属性
System.out.println("==================");
Field[] fields = c1.getFields(); //找到public属性
fields = c1.getDeclaredFields(); //找到全部的属性
for (Field field : fields) {
System.out.println(field);
}
//获得指定属性
System.out.println("====================");
Field name = c1.getDeclaredField("name");
System.out.println(name);
//获得类的方法
System.out.println("=======================");
for (Method method : c1.getMethods()) { //获得本类及其父类的全部public方法
System.out.println(method);
}
System.out.println("+++++++++++++++");
for (Method declaredMethod : c1.getDeclaredMethods()) { //获得本类的所有方法
System.out.println(declaredMethod);
}
//获得指定方法
System.out.println("========================");
Method getName = c1.getMethod("getName", null);
Method setName = c1.getMethod("setName", String.class);
System.out.println(getName);
System.out.println(setName);
//获得构造器
System.out.println("========================");
Constructor[] constructors = c1.getConstructors();
for (Constructor constructor : constructors) {
System.out.println(constructor);
}
//获得指定的构造器
System.out.println("===========================");
Constructor declaredConstructor = c1.getDeclaredConstructor(String.class, int.class,int.class);
System.out.println(declaredConstructor);
}
}
10.6 动态创建对象执行方法
有了Class对象能做什么
- 创建类的对象:调用Class对象的newInstance()方法
- 类必须有一个无参数的构造器
- 类的构造器的访问权限需要足够
思考?难道没有无参构造器就不能创造对象了吗?只要在操作的时候明确的调用类中的构造器,并将参数传递进去之后,就可以实例化操作
- 步骤如下:
- 通过Class类的getDeclaredConstructor(Class ... parameterType)取得本类的指定形参类型的构造器
- 向构造器的形参中传递一个对象数组进去,里面包含了构造器所需的各个参数
- 通过Constructor实例化对象
代码练习
package com.goldtree;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
//动态的创建对象,
public class Test08 {
public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException, NoSuchFieldException {
//获得class对象
Class c1 = Class.forName("com.goldtree.User");
//构造一个对象
Object o = c1.newInstance(); //方法废弃了
Object o2 = c1.getDeclaredConstructor().newInstance();
System.out.println(o2);
//在没有无参构造器的情况下
Constructor declaredConstructor = c1.getDeclaredConstructor(String.class, int.class, int.class);
User maluco = (User) declaredConstructor.newInstance("小丸子", 001, 1);
System.out.println(maluco);
//通过反射调用普通方法
User user = (User) c1.getDeclaredConstructor().newInstance();
//通过反射获得一个方法
Method setNames = c1.getDeclaredMethod("setName", String.class);
Method getNames = c1.getDeclaredMethod("getName");
setNames.invoke(user,"PatrkStar");
System.out.println(getNames.invoke(user));
//或者
user.setName("SongeBob");
System.out.println(user.getName());
//通过反射操作属性
System.out.println("===========================");
User user2 = (User) c1.getDeclaredConstructor().newInstance();
Field name = c1.getDeclaredField("name");
name.setAccessible(true); //name属性是private的,需要开启访问权限
//Method和Field Constructor对象都有setAccessible()方法
name.set(user2,"可爱小丸子");
System.out.println(name.get(user2));
}
}
10.7 性能对比
package com.goldtree;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class Test09 {
public static void main(String[] args) throws InvocationTargetException, NoSuchMethodException, InstantiationException, IllegalAccessException {
test01();
test02();
test03();
}
public static void test01(){
User user = new User();
long begintime = System.currentTimeMillis();
for (int i = 0; i < 1000000000; i++) {
user.getName();
}
long endtime = System.currentTimeMillis();
System.out.println("普通方法执行时间 :" + (endtime-begintime) + " ms");
}
public static void test02() throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
User user = new User();
Class c1 = user.getClass();
User user2 = (User) c1.getDeclaredConstructor().newInstance();
Method getName = c1.getDeclaredMethod("getName",null);
long begintime = System.currentTimeMillis();
for (int i = 0; i < 1000000000; i++) {
getName.invoke(user2);
}
long endtime = System.currentTimeMillis();
System.out.println("反射方法执行时间 :" + (endtime-begintime) +" ms");
}
public static void test03() throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
User user = new User();
Class c1 = user.getClass();
User user3 = (User) c1.getDeclaredConstructor().newInstance();
Method getName = c1.getDeclaredMethod("getName",null);
getName.setAccessible(true);
long begintime = System.currentTimeMillis();
for (int i = 0; i < 1000000000; i++) {
getName.invoke(user3);
}
long endtime = System.currentTimeMillis();
System.out.println("关闭访问检测后反射方法执行时间 :" + (endtime-begintime) +" ms");
}
}
10.8 反射操作泛型
- Java采用泛型擦除的机制来引入泛型,Java中的泛型仅仅是给编译器javac使用的,确保数据的安全性和免去强制类型转换问题,但是一旦编译完成,所有和泛型有关的类型全部擦除
- 为了通过反射操作这些类型,Java新增了ParameterizedType,GenericArrayType,TypeVariable和WildcardType几种类型来代表不能被归一到Class类中的类型,但是又和原始类型齐名的类型
- ParameterizedType:表示一种参数化类型,比如Collection
- GenericArrayType:表示一种元素类型是参数化类型或者类型变量的数组类型
- TypeVariable:是各种类型变量的公共父接口
- WildcardType:代表一种通配符类型表达式
- ParameterizedType:表示一种参数化类型,比如Collection
package com.goldtree;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.List;
import java.util.Map;
//通过反射获取泛型
public class Test10 {
public void test01(Map<String,User> map, List<User> list){
System.out.println("test01");
}
public Map<String,User> test02(){
System.out.println("test02");
return null;
}
public static void main(String[] args) throws NoSuchMethodException {
Method method = Test10.class.getMethod("test01", Map.class, List.class);
Type[] genericParameterTypes = method.getGenericParameterTypes();
for (Type genericParameterType : genericParameterTypes) {
System.out.println(genericParameterType);
if(genericParameterType instanceof ParameterizedType){
Type[] actualTypeArguments = ((ParameterizedType) genericParameterType).getActualTypeArguments();
for (Type actualTypeArgument : actualTypeArguments) {
System.out.println("##" + actualTypeArgument);
}
}
System.out.println();
}
System.out.println("#############################");
System.out.println("#############################");
method = Test10.class.getMethod("test02");
Type genericReturnType = method.getGenericReturnType();
System.out.println(genericReturnType);
if(genericReturnType instanceof ParameterizedType){
Type[] actualTypeArguments = ((ParameterizedType) genericReturnType).getActualTypeArguments();
for (Type actualTypeArgument : actualTypeArguments) {
System.out.println("##" + actualTypeArgument);
}
}
}
}
10.9 反射操作注解
package com.goldtree;
import java.lang.annotation.*;
import java.lang.reflect.Field;
public class Test11 {
public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException {
Class c1 = Class.forName("com.goldtree.Student2");
//通过反射获得注解
for (Annotation annotation : c1.getAnnotations()) {
System.out.println(annotation);
}
//获得注解value的值
Tablegoldtree tablegoldtree = (Tablegoldtree) c1.getAnnotation(Tablegoldtree.class);
String value = tablegoldtree.value();
System.out.println("=====================");
System.out.println(value);
//获得类指定的注解
System.out.println("=========================");
Field f = c1.getDeclaredField("name");
Fieldgoldtree annotation = f.getAnnotation(Fieldgoldtree.class);
System.out.println(annotation);
String s = annotation.columnName();
System.out.println(s);
System.out.println(annotation.length());
System.out.println(annotation.type());
}
}
@Tablegoldtree("db_student")
class Student2{
@Fieldgoldtree(columnName = "db_id",type = "int",length = 10)
private int id;
@Fieldgoldtree(columnName = "db_age",type = "int",length = 10)
private int age;
@Fieldgoldtree(columnName = "db_name",type = "varchar",length = 3)
private String name;
public Student2() {
}
public Student2(int id, int age, String name) {
this.id = id;
this.age = age;
this.name = name;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Student2{" +
"id=" + id +
", age=" + age +
", name='" + name + '\'' +
'}';
}
}
//类名的注解
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@interface Tablegoldtree{
String value();
}
//属性的注解
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@interface Fieldgoldtree{
String columnName();
String type();
int length();
}
11 注解
什么是注解
package com.goldtree;
//什么是注解
public class Test01 extends Object{
//@Override 重写的注解
@Override
public String toString() {
return super.toString();
}
}
元注解
- 元注解的作用就是负责注解其他注解,Java定义了4个标准的meta-annotation类型,他们被用来提供对其他annotation类型做说明
- 这些类型和他们所支持的类在java.lang.annotation包中可以找到
- @Target:用于描述注解的使用范围
- @Retention:表示需要在什么级别保存该注解信息,用于描述注解的声明周期
- @Document:说明该注解将被包含在javadoc中
- @Inherited:说明子类可以继承父类中的该注解
package com.goldtree;
import java.lang.annotation.*;
//测试元注解
@MyAnnotation
public class Test02 {
@MyAnnotation
public void test(){
}
}
//定义一个注解
//Target表示注解可以用在哪些地方
@Target(value = {ElementType.METHOD,ElementType.TYPE})
//Retention表示注解在什么地方有效
//runtime > class > sources
@Retention(value = RetentionPolicy.RUNTIME)
//表示是否将注解生成在javadoc中
@Documented
//Inherited表示子类可以继承父类注解
@Inherited
@interface MyAnnotation{
}
自定义注解
- 使用 @interface自定义注解时,自动继承了java.lang.annotation.Annotation接口
- 分析
- @interface用来声明一个注解,格式:public @interface 注解名
- 其中的每一个方法实际上市声明了一个配置参数
- 方法名称就是参数名称
- 返回值类型就是参数的类型(返回值只能是基本类型,Class,String,enum)
- 可以通过default来声明参数的默认值
- 如果只有一个参数成员,一般参数名为value
- 注解元素必须要有值,我们定义注解元素时,经常使用空字符串,0作为默认值
package com.goldtree;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
public class Test03 {
//注解可以显示赋值
@MyAnnotation2(name = "goldtree",schools = {"小学","大学"})
public void test(){}
@MyAnnotation3("SpongeBob")
public void test2(){}
}
@Target({ElementType.TYPE,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@interface MyAnnotation2{
//注解的参数 :参数类型 + 参数名();不是方法
String name() default ""; //有default后,注解使用的时候可以不写参数
int age() default 0;
int id() default -1; //如果默认值为-1,代表不存在
String[] schools() ;
}
@Target({ElementType.TYPE,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@interface MyAnnotation3{
String value();
}
12 AOP
12.1 什么是AOP
AOP(Aspect Oriented Programming)意为:面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。AOP是OOP的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率
12.2 Aop在Spring中的作用
提供声明式事务;允许用户自定义切面
- 横切关注点:跨越应用程序多个模块的方法或功能,与我们业务逻辑无关的,但是我们需要关注的部分,就是横切关注点,如日志,安全,缓存,事务等等 ...
- 切面(ASPECT):横切关注点被模块化的特殊对象,它是一个类
- 通知(Advice):切面必须要完成的工作,它是类中的一个方法
- 目标(Target):被通知的对象,是一个接口或者一个方法
- 代理(Proxy):向目标对象应用通知后创建的对象
- 切入点(PointCut):切面通知执行的“地点”的定义
- 连接点(jointPoint):与切入点匹配的执行点
12.3 使用Spring实现Aop
【重点】使用AOP织入,需要导入一个依赖包
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.4</version>
</dependency>
测试准备
创建一个接口
package com.goldtree.service;
public interface UserService {
public void add();
public void del();
public void update();
public void select();
}
创建一个实现类
package com.goldtree.service;
public class UserServiceImpl implements UserService{
public void add(){
System.out.println("增加了一个用户");
}
public void del(){
System.out.println("删除了一个方法");
}
public void update(){
System.out.println("修改了一个方法");
}
public void select(){
System.out.println("查询了一个方法");
}
}
12.3.1 方式1 :使用Spring的API接口实现
package com.goldtree.log;
import org.springframework.aop.MethodBeforeAdvice;
import java.lang.reflect.Method;
public class log implements MethodBeforeAdvice {
//Method:要执行的目标对象的方法
//args:参数
//target:目标对象
@Override
public void before(Method method, Object[] args, Object target) throws Throwable {
System.out.println(target.getClass().getName()+"的"+method.getName()+"被执行");
}
}
package com.goldtree.log;
import org.springframework.aop.AfterReturningAdvice;
import java.lang.reflect.Method;
public class AfterLog implements AfterReturningAdvice {
//returnValue返回值
@Override
public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
System.out.println("执行了"+method.getName()+"方法返回结果为:"+returnValue);
}
}
<?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"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<!-- 注册bean-->
<bean id = "userService" class="com.goldtree.service.UserServiceImpl"/>
<bean id = "log" class="com.goldtree.log.log"/>
<bean id = "afterLog" class="com.goldtree.log.AfterLog"/>
<!-- 方式一:使用原生Spring API接口-->
<!-- 配置AOP 需要导入aop的约束-->
<aop:config>
<!-- 切入点 expression="excution()-->
<aop:pointcut id="pointcut" expression="execution(* com.goldtree.service.UserServiceImpl.*(..))"/>
<!-- 执行环绕增强-->
<!-- 在log类引用pointcut切入点-->
<aop:advisor advice-ref="log" pointcut-ref="pointcut"/>
<aop:advisor advice-ref="afterLog" pointcut-ref="pointcut"/>
</aop:config>
</beans>
import com.goldtree.service.UserService;
import com.goldtree.service.UserServiceImpl;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class MyTest {
public static void main(String[] args) {
ApplicationContext Context = new ClassPathXmlApplicationContext("ApplicationContext.xml");
//动态代理代理的是接口
UserService userservice = Context.getBean("userService",UserService.class);
userservice.select();
}
}
12.3.2 方式二:自定义来实现AOP
自定义一个pointcut类
package com.goldtree.Diy;
public class diyPointut {
public void before(){
System.out.println("===========方法执行前=========");
}
public void after(){
System.out.println("===========方法执行后=========");
}
}
配置bean
<?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"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<!-- 注册bean-->
<bean id = "userService" class="com.goldtree.service.UserServiceImpl"/>
<bean id = "log" class="com.goldtree.log.log"/>
<bean id = "afterLog" class="com.goldtree.log.AfterLog"/>
<!-- 方法二:自定义类-->
<bean id = "diy" class="com.goldtree.Diy.diyPointut"/>
<aop:config>
<!-- 自定义切面,ref 要引用的类-->
<aop:aspect ref="diy">
<!-- 切入点-->
<aop:pointcut id="point" expression="execution(* com.goldtree.service.UserServiceImpl.*(..))"/>
<!-- 通知-->
<aop:before method="before" pointcut-ref="point"/>
<aop:after method="after" pointcut-ref="point"/>
</aop:aspect>
</aop:config>
</beans>
方式三:使用注解实现AOP
定义一个注解类
package com.goldtree.Diy;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
//方式三:使用注解实现AOP
@Aspect //标注这个类是一个切面
public class AnnotationPointCut {
@Before("execution(* com.goldtree.service.UserServiceImpl.*(..))")
public void before(){
System.out.println("++++++方法执行前+++++++");
}
@After("execution(* com.goldtree.service.UserServiceImpl.*(..))")
public void after(){
System.out.println("++++++方法执行后+++++++");
}
//在环绕增强中,我们可以给定一个参数,代表我们要获取处理切入的点
@Around("execution(* com.goldtree.service.UserServiceImpl.*(..))")
public void around(ProceedingJoinPoint jp) throws Throwable {
System.out.println("++++++环绕前+++++++");
Signature signature = jp.getSignature();//获得签名
System.out.println("signature: " + signature);
//执行方法
Object proceed = jp.proceed();
System.out.println("环绕后");
System.out.println(proceed);
}
}
定义bean
<?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"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<!-- 注册bean-->
<bean id = "userService" class="com.goldtree.service.UserServiceImpl"/>
<bean id = "log" class="com.goldtree.log.log"/>
<bean id = "afterLog" class="com.goldtree.log.AfterLog"/>
<!--方式三:-->
<bean id = "annotationPointCut" class="com.goldtree.Diy.AnnotationPointCut"/>
<!-- 开启注解支持-->
<aop:aspectj-autoproxy/>
</beans>