快捷键
alt+insert 自动设置set/get/重写等方法
alt+enter 自动导入依赖包
javaSE学习
博客的重要性
- 总结和思考
- 更有效的记忆
- 提升文笔组织能力
MarkDown语法
文件后缀 .md
常用的有标题,引用,列表,图片,超链接,无序列表,表格,代码
冯诺依曼体系结构
运算器,存储器,控制器,输入,输出
计算机软件
系统软件
操作系统:DOS(Disk Operating System),Windows, Linux, Unix, Android, IOS
应用软件
DOS命令
dir #效果和ls一样
cd #切换目录
cls #清屏命令
exit #退出
ipconfig#查看IP
md #创建目录
rd #移除目录
cd>文件名#创建文件
del #删除文件
java特性
- 简单
- 面向对象
- 可移植性,跨平台 Write Once, Run Anywhere
- 分布式
- 高性能
java三大版本:JavaSE标准版、JavaME嵌入式开发、JavaEE企业级开发
JDK、JRE、JVM的关系,从大到小的关系如下
- JDK : Java Development Kit
- JRE : Java Runtime Environment
- JVM : Java Virtual Machine
第一个java程序
新建Hello.java文件,其内容如下
public class Hello {//Hello类名应该与文件名称Hello.java相同,否则会报错
public static void main(String[] args) {
System.out.print("Hello World!");
}
}
在当前目录下打开cmd,输入如下命令
javac Hello.java #作用是使用javac进行编译,生成Hello.class文件
java Hello #作用是使用java运行字节码文件Hello.class文件,输出Hello World!
#注意:写成java Hello.class会出错
注意:文件名和类型必须一致,并且首字母大写
java运行机制
编译型
解释型
java基础语法
注释
三种注释:单行注释、多行注释、文档注释
单行注释://
多行注释:/* */
文档注释:javaDoc
标识符
所有的标识符都应该以字母(A-Z或者a-z)、**美元符号($)**或者下划线(_)开始,
首字符之后可以是字母(A-Z或者a-z)、美元符号($)、下划线(_)或者数字的任何组合字符。
大小写敏感
数据类型
强类型语言:所有变量都必须先定义后才能使用,安全性高
java数据类型分为两大类
-
基本类型
类型 名称 占据空间大小,单位字节 整数 byte 1 整数 short 2 整数 int 4 整数 long 8 浮点数 float 4 浮点数 double 8 字符 char 2 布尔类型 boolean 1bit //long类型要在数字后面加个L long num1 = 300L; //float类型要在数字后面加个F float num2 = 50.1F;
-
引用类型
类,接口,数组
类型转换
强制类型转换 高–>低
char c = 'a';
int a = (int)c;//强制转换类型
long aa = 234935234332L;
int bb = 12334;
short cc = 133;
byte dd = 23;
System.out.print(a + b + c + d); //输出是long类型
System.out.print(b + c + d); //输出是int类型
System.out.print(c + d); //输出是int类型,原因:没有long型时候,所有非int型转为int型
自动类型转换 低–>高 自动转换
注意:不能对布尔类型进行转换,布尔类型不能参与类型转换
变量
变量是基本的存储单元,要素包括变量名、变量类型和作用域。
public class Demo {
//类变量 static 从属于类
static double salary = 20000.0;
//实例变量:从属于对象,有默认值 0 0.0 null flase
String name;
int age;
//main方法
public static void main(String[] args) {
//局部变量
int i = 0;
Demo demo = new Demo();
System.out.print(demo.name);//String对象的默认值是null
System.out.print(demo.age);
}
//其他方法
public void func() {
}
}
常量
初始化后不能再改变的值,在程序运行过程中不允许被改变, 关键字final
final 类型 常量名 = 值;
final double PI = 3.14;//常量名一般用大写字符
运算符
算术运算符 +、-、*、/、%、++、–
赋值运算符 =
关系运算符 >、<、>=、<=、==、!=
逻辑运算符 &&、||、 !
逻辑运算符具&&和II有短路性质
位运算符 &(按位与)、|(按位或)、^(按位异或)、~(按位取反)、>>、<<、>>>
条件运算符 ?:
扩展运算符 +=、-=、*=、/=
int a = 10;
int b = 20;
//字符串连接使用“+”
System.out.print("" + a + b);//输出1020
System.out.print(a + b + "");//输出30
包机制
为了更好的组织类,java提供了包机制,用于区别类名的命名空间
包语句的语法格式如下
package pkg1[.pkg2[.pkg3···]];
//该语句必须在整个类的最开始处
一般利用公司域名倒置作为包名,如com.baidu.www
使用import语句导入包
import package1[.package2···].(classname|*);//*号代表所有
javaDoc
javadoc命令可以用来生成自己的API文档,使用命令如下
javadoc -encoding utf-8 -charset utf-8 HelloWorld.java
常用的注解 参数信息
// 在方法上输入/**然后回车,即可生成注解
/*
@author 作者名
@version 版本号
@since 指明需要最早使用的jdk版本 如1.8
@param 参数名
@return 返回值情况
@throws 异常抛出情况
*/
java流程控制
- Scanner
import java.util.Scanner;
//从键盘接收数据
Scanner scan = new Scanner(System.in);
//接受输入
String str = scan.next();
System.out.println(str);
//注意,使用完之后一定要关闭
scan.close();
-
顺序结构
-
if选择结构
-
switch case选择结构
-
while循环结构
-
do while循环结构
-
for循环结构
for (int num : nums) { System.out.println(num); } for (int )
Java方法
方法
java方法的命名规则:首字母小写+驼峰命名法,如
方法包含于类或者对象中,在程序中创建,在其他地方被引用
设计方法时候最好保持方法的原子性,一个方法只完成一个功能
修饰符 返回值类型 方法名(参数类型 参数名) {
···
方法体
···
return 返回值;
}
参数列表指的是方法的参数类型、顺序和参数个数
调用方法:对象名.方法名(实参列表)
值传递
java只有值传递
方法重载
重载是在一个类中,有相同的函数名称,但是形参不同的函数
重载规则如下:
- 方法名称必须相同
- 参数列表不同(个数,类型或者排列顺序不同)
- 方法的返回类型可以相同也可以不同
- 仅返回类型不同不是重载, 会报错,原因:方法重定义
方法名称相同时候,编译器会根据调用方法的参数个数,参数类型去逐个匹配,如果匹配失败会报错
命令行传参
//通过main方法传入参数
public static void main(String[] args) {
for (int i = 0; i < args.length; i ++)
{
System.out.println("args[" + i + "]:" + args[i]);
}
return;
}
运行方法:
首先在命令行用javac将.java代码编译成.class文件,然后跳转到src目录下,使用java命令运行,此时文件需要带包前缀,并在后面跟上自己要传入的参数
java test.CommandLine this is test//test是包名,里面有CommandLine类
可变参数
JDK1.5开始,Java支持传递同类型的可变参数的一个方法,在指定参数类型后加一个省略号(…)
一个方法中只能制定一个可变参数 ,它必须是方法的最后一个参数,任何普通参数必须在它之前声明
public static void main(String[] args) {
int max = getMax(1,2,3,4,5);
System.out.print(max);
return;
}
public static int getMax(int... x)//可变参数,在参数类型后加...
{
int res = 0;
for (int i = 0; i < x.length; i ++)
if (res < x[i]) res = x[i];
return res;
}
递归
递归结构包括两个部分:
- 递归头:什么时候不调用自身方法。如果没有头,将陷入死循环
- 递归体:什么时候需要调用自身方法
数组
数组
数组是相同类型数据的有序集合,通过下标来进行访问
数组声明方法:
//使用new操作符来创建数组
dataType[] arrayRefVar = new dataType[arraySize];
int[] array = new int[10];//创建长度为10的int型数组
数组的特点
下标的合法区间[0, nums.length - 1],如果越界会报错
ArrayIndexOutOfBoundsException:数组下标越界异常
数组的三种初始化方式
-
静态初始化
int[] nums = {1,2,3}; Man[] mans = {new Man("Jerry"), new Man("Tom")};
-
动态初始化
int[] nums = new int[10];//此时每个元素被隐式初始化为0; int[] nums2 = new int[]{1,2,3,4,5};//使用元{元素}初始化时候,不能指派数组的长度,不然报错
-
数组的默认初始化
数组是引用类型,它的元素相当于类的实例变量,因此数组一经分配空间,其中的每个元素都会被隐式初始化为默认值。
二维数组
int[][] nums = new int[2][3];//定义2行3列二维数组
int[][] nums = new int[][]{{1,2},{3,4},{5,6}};
for (int i = 0; i < nums.length; i ++)
for (int j = 0; j < nums[0].length; j ++)
System.out.print(nums[i][j] + " ");
Arrays工具类
import java.util.Arrays;
int[] a = new int[]{3,2,56,6};
Arrays.sort(a);//调用Arrays工具类对数组进行排序
System.out.print(Arrays.toString(a));//以字符串的形式输出数组
//输出如下
[2, 3, 6, 56]
排序算法
- 冒泡排序
稀疏数组
原因:因为该二维数组中很多默认值是0,因此记录了很多没有意义的数据,解决方案使用稀疏数组
面向对象编程
面向对象
面向对象的思想:物以类聚
OOP:Object-Oriented Programming 面向对象编程
三大特征:封装,继承,多态
对象是类的实例化,是具体的事物,类是对象的抽象。
类是一种抽象的数据类型,它是对某一类事物整体描述/定义,但是不能代表一个具体的事物。
创建对象
-
使用new关键字创建对象
Dog dog = new Dog();//dog是一个引用变量名,真正的数据存放在堆区
使用new创建对象时候,除了分配内存之外,还会给创建好的对象进行默认的初始化及对类中构造器的调用
类中会自动生成无参默认构造器,如果自定义构造器之后,则原来的无参构造器就不存在了
构造方法的特点如下:
- 必须和类的名字相同
- 必须没有返回类型
public class Student {
String name;
int age;
public Student(String _name, int _age)
{
this.name = _name;//也可以不写this
age = _age;
}
}
- 类里面包含两种东西:静态的属性和动态的方法
封装
通常应该禁止直接访问一个对象中数据的实际表示,而应该通过接口操作来访问,这称为信息隐藏
高内聚,低耦合
属性私有,使用get/set来进行访问/设置
私有private
封装的好处:
- 提高程序的安全型
- 隐藏代码的实现细节
- 统一接口
- 系统可维护性增加
继承
所有类都直接或者间接继承Object类
关键字extends,子类继承父类,使用关键字extends来表示
java只有单继承,没有多继承,但一个类可以被多个类继承
public class Person {
String name;
}
public class Doctor extends Person {//Doctor继承Person
void fun();
}
super
super注意点
- super调用父类的构造方法,必须在构造方法的第一个
- super必须只能出现在子类的方法或者构造方法中
- super和this不能同时调用构造方法
和this的区别
- this代表调用者本身,super代表父类对象的引用
- this没有继承也可以使用,super只在有继承条件下才使用
- 构造方法:this()本类的构造,super()父类的构造
public class Person {
String name;
Person(String _name) {this.name = _name;}
}
public class Doctor extends Person {//Doctor继承Person
String career;
Doctor(String _name, String _career) {
super(_name);
this.carrer = _career;
}
void fun();
}
重写
在继承关系中,子类重写父类的方法
- 方法名必须相同
- 参数列表必须相同
- 修饰符:范围可以扩大,但不能缩小:public > protected > default > private
- 抛出的异常:范围可以被缩小不能被扩大:ClassNotFoundException --> Exception(大)
重写函数的快捷键操作:Alt + Insert,然后选择 override;会生成@Override注解
public class Person {
public void eat() {System.out.println("Person eat")}
}
public class Doctor extends Person {
@Override//表示是子类重写父类的方法
public void eat() {
System.out.println("Doctor eat");
}
}
多态
同一方法可以根据发送对象的不同二采区多种不同的行为方式。
一个对象的实际类型是确定的,但可以指向对象的引用的类型有很多(父类,有关系的类)
多态存在的条件
- 有继承关系
- 子类重写父类方法
- 父类引用指向子类对象
Father f = new Son();//父类引用指向子类对象
final关键字:修饰的变量是常量,修饰的函数不能被重写
多态是方法的多态,属性没有多态
instanceof
作用:判断两个类之间是否存在父子关系,有父子关系返回true,没有返回false
//继承链如下:
//Object->Person->Student
//Object->Person->Teacher
Object obj = new Student();
System.out.println(obj instanceof Student);//true
System.out.println(obj instanceof Person);//true
System.out.println(obj instanceof Object);//true
System.out.println(obj instanceof Teacher);//false
//总结
System.out.println(X instanceof Y);
//如果X和Y之间不存在父子关系,则编译出错
//当满足父子关系时候,如果X的实际类型是Y类型或者其子类型时候,则返回true,否则返回false
static静态代码块
在类中,用static修饰的代码块只在类加载的时候执行一次,而不用static修饰的代码块则在创建对象时候都执行
public class Person {
{
System.out.println("匿名代码块");
}
static {
System.out.println("静态代码块");
}
Person () {
System.out.println("构造方法");
}
public static void main(String[] args) {
System.out.println("===========");
Person p1 = new Person();
System.out.println("===========");
Person p2 = new Person();
}
}
//输入如下
静态代码块
===========
匿名代码块
构造方法
===========
匿名代码块
构造方法
抽象类
使用关键字abstract来表示
//使用关键字abstract表示某个类是抽象类
public abstract class Action {
public abstract void func();//使用abstract表明某个方法是抽象方法,只有名字,没有实现
//不能new抽象类,只能靠子类实现这个抽象方法之后,才能实例化对象
public void do() {
System.out.println("do");
}
//抽象类中可以写普通的方法
}
接口
接口里面只有方法的定义,没有方法的实现
使用interface关键字来定义接,使用implments关键字来实现接口的继承
//接口类使用interface关键字,此处没有class关键字
public interface Action {
//接口中的所有量都是常量,是public static final的
int AGE = 99;//等价与public static final int AGE = 99;
//接口中的方法都是public abstract的
void add();
void delete();//等价于public abstract void delete();
}
//类可以实现接口,使用implements关键字
//实现了接口的类,要重写里面的方法
public class ActionImpl implements Action{
@Override
public void add() {
System.out.println("add");
}
@Override
public void delete() {
System.out.println("delete");
}
}
implements可以实现多个接口
接口不能被实例化,接口中没有构造方法
普通类vs抽象类vs接口
普通类:含有方法的具体实现
抽象类:含有抽象方法,无具体实现,也可以含有具体实现
接口:只有抽象方法(规范)
声明类的关键字是class,声明接口的关键字是interface
内部类
内部类就是在一个类内部再定义一个类,比如A类中定义一个B类,B类相对于A类来说就是内部类,而A类对B类来说就是外部类。
- 成员内部类
- 静态内部类
- 局部内部类
- 匿名内部类
一个java类中可以i有多个class类,但是只能有一个public class类
异常
java把异常当做对象来处理,并定义一个基类java.lang.Throwable作为所有异常的超类
主要分为两种错误Error和异常Exception
异常处理
- 抛出异常
- 捕获异常
异常处理五个关键字:try、catch、finally、throw、throws
int a = 1;
int b = 0;
try { //try监控区域
System.out.print(a / b);
}catch (ArtimeticException e) { //catch 捕获异常
System.out.print("程序出现异常,变量b不能为0");
}finally { //处理善后工作,不管是否进行都会执行,可以进行一些资源的释放等等
System.out.print("程序结束");
}
throw用于程序抛出异常
throws用于方法抛出异常
另外假设要捕获多个异常,从小到大,范围逐渐扩大,或者互不相交
Spring学习
spring框架
spring
spring是一个轻量级控制反转(IOC)和面向切面(AOP)的容器框架
SSM:SpringMVC + Spring + Mybatis;
Spring的优点
- 开源的免费框架
- 轻量级、非入侵式的框架
- 控制反转、面向切面编程
- 支持事务,对框架整合的支持
spring组成
IOC理论推导
使用set接口实现
private UserDao userDao;
//利用set进行动态实现值的注入!
public void setUserDao(UserDao _userDao) {
this.userDao = _userDao;
}
- 之前,程序主动创建对象,控制权在程序员手上
- 使用了set注入之后,程序不再具有主动性,而是变成了被动的接受对象!
附IOC简单模型源代码,程序结构如下:
三层结构
UserDao 接口
UserDaoImpl 实现类
UserService 业务接口
UserServiceImpl 业务实现类
其中代码为
//从上到下的代码依次为
public interface UserDao {
void getUser();
}
public class UserDaoImpl implements UserDao{
@Override
public void getUser() {
System.out.println("默认获取用户");
}
}
public class UserDaoMysqlImpl implements UserDao {
@Override
public void getUser() {
System.out.println("Mysql获取用户");
}
}
public class UserDaoOracleImpl implements UserDao {
@Override
public void getUser() {
System.out.println("Oracle 获取用户");
}
}
public class UserDaoSqlserverImpl implements UserDao {
@Override
public void getUser() {
System.out.println("Sqlserver获取用户");
}
}
//业务层
public interface UserService {
void getUser();
}
public class UserServiceImpl implements UserService{
private UserDao user;
//set注入实现控制反转
public void setUserDao(UserDao _user) {
this.user = _user;
}
@Override
public void getUser() {
user.getUser();
}
}
//用户层,这样做的好处是将控制权转到用户层
public class MyTest {
public static void main(String[] args) {
UserService userService = new UserServiceImpl();
//当想调用不同的UserDao方法时候,只需要使用set注入不同的UserDao实现类的对象
((UserServiceImpl) userService).setUserDao(new UserDaoSqlserverImpl());
userService.getUser();
}
}
IOC本质
**控制反转(IOC),是一种设计思想,DI(依赖注入)是实现IOC的一种方法。**没有loC的程序中,我们使用面向对象编程,对象的创建与对象间的依赖关系完全硬编码在程序中,对象的创建由程序自己控制,控制反转后将对象的创建转移给第三方,个人认为所谓控制反转就是:获得依赖对象的方式反转了。
控制反转是一种通过描述(XML或注解)并通过第三方去生产或获取特定对象的方式。在Spring中实现控制反转的是loC容器,其实现方法是依赖注入(Dependency Injection,Dl)。
控制:谁来控制对象的创建,传统应用程序的对象是由程序本身控制创建的,使用Spring之后,对象是由Spring来创建的
反转:程序本身不创建对象,而变成被动的接收对象
依赖注入:就是利用set方法来进行注入
IOC是一种编程思想,由主动的编程变成被动的接收
不用去程序中改动了,要实现不同的操作,只需要在xml配置文件中进行修改,所谓的IOC:对象由Spring来创建、管理、 装配!
创建并使用Spring
配置bean.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">
<!--com.java.spring5.User类中有两个属性,一个是age,另外一个是name-->
<!--通过property设置属性的前提是类中含有该属性的set方法-->
<!--配置User对象创建-->
<bean id = "user" class = "com.java.spring5.User">
<property name = "age" value = "3"/>
<property name = "name" value = "tom" />
</bean>
</beans>
java中调用bean.xml的用法
public static void main(String[] args) {
//标准写法如下
ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");
//从IOC容器中获取spring创建的user对象
User user = (User)context.getBean("user");
//调用user对象的show()方法
user.show();
}
IOC创建对象的方式
-
默认使用无参构造方法创建对象,之后使用标签来set对象内的参数
-
当没有默认构造方法时候,必须使用有参构造对象时候,使用标签来设置参数
-
下标赋值
<bean id = "user2" class = "com.java.spring5.User"> <constructor-arg index="0" value="3"/> <constructor-arg index="1" value="tom"/> </bean>
-
类型赋值(不推荐,而且当含有相同类型的变量时候,该方法失效)
<bean id = "user3" class = "com.java.spring5.User"> <constructor-arg type="int" value="3"/> <constructor-arg type="java.lang.String" value="tom"/> </bean>
-
参数名称赋值(推荐)
<bean id="user" class="com.java.spring5.User"> <constructor-arg name="age" value="3"/> <constructor-arg name="name" value="tom" /> </bean>
-
总结:在配置文件加载的时候,容器中管理的 对象就已经初始化了!
Spring配置
alias别名
<!--别名,如果使用了别名,可以使用别名去获取到这个对象-->
<alias name="user" alias="userNew" />
bean配置
<!--
id:bean的唯一标识符,也就是相当于我们的对象名称
class:bean对象所对应的全限定名:包名+类型
name:也是别名,而且name可以同时取多个别名
-->
<bean id="user" class="com.java.spring5.User" name="u,u2,u3">
<property name="name" value="abc"/>
</bean>
import导入
import一般用于团队开发中,可以将多个配置文件,导入合并成一个总的
把beans1.xml、beans2.xml、beans3.xml导入到applicationContext.xml中,形成一个总的,这样使用的时候,直接使用总的就可以了
<import resource="beans1.xml"/>
<import resource="beans2.xml"/>
<import resource="beans3.xml"/>
依赖注入
构造器注入
注入
set方式注入【重点】
注入
依赖注入
- 依赖:bean对象的创建依赖于容器
- 注入:bean对象中的所有属性,由容器来注入
举例:各种数据结构的注入方式
public class Address {
private String address;
public Address(String address)
{
this.address = address;
}
//此处set/get以及to_string等没有体现出来
}
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;
//此处set/get以及to_string等没有体现出来
}
<?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.java.demo1.Address">
<constructor-arg name="address" value="四川成都"/>
</bean>
<bean id="student" class="com.java.demo1.Student">
<!--String,使用value-->
<property name="name" value="lufy"/>
<!--引用另外的bean,使用ref-->
<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="身份证" value="111111222222223333"/>
<entry key="银行卡" value="23423875927349729374823"/>
</map>
</property>
<!--Set-->
<property name="games">
<set>
<value>LOL</value>
<value>植物大战僵尸</value>
</set>
</property>
<!--null-->
<property name="wife">
<null/>
</property>
<!--Properties-->
<property name="info">
<props>
<prop key="username">root</prop>
<prop key="passward">123456</prop>
</props>
</property>
</bean>
</beans>
扩展方式注入
可以使用p命名空间和c命名空间注入
注意:p命名和c命名空间不能直接使用,需要导入约束!
xmlns:c="http://www.springframework.org/schema/c"
xmlns:p="http://www.springframework.org/schema/p"
使用方法
<!-- p命名空间注入:可以直接注入属性的值:property -->
<bean id="user" class="com.java.demo.User" p:name="小明" p:age=18>
<!-- c命名空间注入:通过构造器注入:constructor-arg -->
<bean id="user2" class="com.java.deom.User" c:name="小红" c:age=18>
其实说白了就是set方式注入和构造器注入的简化写法
bean的作用域
-
单例模式:此时在java程序中利用getBean(“user”)获取两次,会得到同一个对象
<bean id="user" class="com.java.demo.User" scope="singleton"/>
-
原型模式:每次从容器中get时候,都会产生一个新对象!
xxxxxxxxxx <bean id="user" class="com.java.demo.User" scope="prototype"/>
-
其余的request、session、application,这些只能在web开发中使用
bean的自动装配
自动装配是Spring满足bean依赖的一种方式
Spring会自动在上下文中寻找,并自动给bean装配属性!
在spring中有三种装配的方式
<bean id="cat" class="com.java.Cat" />
<bean id="dog" class="com.java.Dog"/>
- 在xml中显示装配
<!--在xml中显示装配 -->
<bean id="people" class="com.java.People">
<property name="cat" ref="cat"/>
<property name="dog" ref="dog"/>
</bean>
- 在java中显示配置(此处未介绍)
- 隐式的自动装配bean【重要】:有byName和byType两种装配形式
<!--autowire="byName" 会在容器上下文中查找,和自己对象set方法里的参数名称相同的beanid!-->
<bean id="people" class="com.java.People" autowire="byName" />
<!--autowire="byType"会自动在容器上下文中查找,和自己对象属性类型相同的bean,同一种类型需要全局唯一-->
<bean id="people" class="com.java.People" autowire="byType" />
小结:
- byName时候,需要保证所有bean的id唯一,并且这个id需要和自动注入的属性的set方法的值一致!
- byType时候,需要保证所有bean的class唯一,并且这个bean需要和自动注入的属性的类型一致!
使用注解实现自动装配
使用注解须知:
- 导入约束: contex约束
- 配置注解的支持: 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
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>
@Autowired注解
直接在属性上使用即可!也可以在set方法上使用
使用Autowired我们可以不用编写set方法了,前提是你这个自动装配的属性在IOC(Spring)容器中存在,且符合名字byname!
测试代码
public class People {
@Autowired
@Qualifier(value="cat")
private Cat cat;
@Autowired
private Dog dog;
private String name;
}
如果@Autowired自动装配的环境比较复杂,自动装配无法通过一个注解【@Autowired】完成的时候,我们可以使用添加@Qualifier(value=“xxx”)去配置@Autowired的使用,指定一个唯一的bean对象注入!
@Resource注解
测试代码
public class People {
@Resource(name="cat")
private Cat cat;
@Resource
private Dog dog;
private String name;
}
小结:
@Autowired和@Resource的区别
- 都是用来自动装配的,都可以放到属性字段上
- @Autowired通过byType的方式实现【常用】
- @Resource默认通过byName的方式实现,如果找不到名字,则通过byType实现【常用】
Spring注解开发
开启注解支持
<!--指定要扫描的包,这个包下的注解就会生效-->
<context:component-scan base-package="com.java" />
<context:annotation-config/>
@Component注解
@Component
public class User {
public String name;
//相当于<property name="name" value="tantu" />
@value("tantu")
public void setName(String name)
{
this.name = name;
}
}
衍生的注解
@Component 有几个衍生注解,在web开发中,会按照mvc三层架构分层!
- dao 【@Repository】
- service 【@Service】
- controller 【@Controller】
这四个注解的功能都是一样的,都是代表将某个类注册到Spring中,装配Bean
小结
xml与注解:
- xml更加万能,适用于任何场合!维护简单方便
- 注解 不是自己类使用不了,维护相对复杂!
xml和注解最佳实践
- xml用来管理bean
- 注解只负责完成属性的注入
- 使用过程中需要注意一个问题:必须让注解生效,开启注解的支持(见上面)
代理模式
代理模式的好处:
- 可以使得真实角色的操作更加纯粹,不用去关注一些公共的业务
- 公共业务交给了代理角色,实现了业务的分工
- 公共业务发生扩展的时候,方便集中管理
缺点:一个真实角色就会产生一个代理角色,代码量会翻倍,开发效率降低
AOP
动态代理
public class ProxyInvocationHandler implements InvocationHandler {
//被代理的接口
private Object target;
public void setTarget(Object target) {
this.target = target;
}
//生成得到代理类
public Object getProxy() {
return Proxy.newProxyInstance(this.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
}
//处理代理实例,并返回结果
public Object invoke(Object proxy, Method method, Objcet[] args) throws Throwable {
//动态代理的本质,就是使用反射机制实现!
Object result = method.invoke(target, args);
return result;
}
}
public static void main(String[] args) {
//真实角色
UserServiceImpl userService = new UserServiceImpl();
//代理角色
ProxyInvocationHandler pih = new ProxyInvocationHandler();
//设置要代理的对象
pih.setTarget(userService);
//动态生成代理类
UserService proxy = (UserService)pih.getProxy();
proxy.func();//调用代理类的实际方法
}
需要了解两个类:Proxy:代理 InvocationHandler:调用处理程序
动态代理的好处:
- 可以使得真实角色的操作更加纯粹,不用去关注一些公共的业务
- 公共业务交给了代理角色,实现了业务的分工
- 公共业务发生扩展的时候,方便集中管理
- 一个动态代理类代理的是一个接口,一般就是对应的一类业务
- 一个动态代理类可以代理多个类,只要是实现了同一个接口即可!