【2022年Spring全家桶】Spring5.x 框架详解
本文是在看了b站视频【尚硅谷Spring框架视频教程(spring5源码级讲解)】后,手动实现及理解整理
视频地址:https://www.bilibili.com/video/BV1Vf4y127N5?spm_id_from=333.999.0.0
Spring框架概述
Spring是轻量级开源JavaEE框架,可以解决企业应用开发的困难性。
有两个核心部分:
- IOC:控制反转, 把创建对象的过程交给Spring去管理
- AOP: 面向切面编程,不修改源码进行功能增强。
Spring 特点:
- 方便解耦、简化开发
- AOP编程支持
- 方便程序测试
- 方便和其他框架整合
- 方便进行事务操作
- 降低API开发难度
Spring 模块
入门案例
1.导入IOC核心jar包
2.创建对象类
public class Book {private String bName;private String bAuthor;public void testDemo() {System.out.println(bName + "::" + bAuthor);}
}
3.创建bean1.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="book" class="cn.lych4.spring5.bean.Book"></bean>
</beans>
4.创建对象
//1. 加载spring 配置文件ApplicationContext context = new ClassPathXmlApplicationContext("bean1.xml");//2.获取配置的对象Book book = context.getBean("book", Book.class);System.out.println(book);book.testDemo();
IOC
什么是IOC?
控制反转(IOC), 就算把对象创建和对象之间的调用过程交给Spring容器进行管理
IOC的目的是为了降低耦合
IOC过程
第一步: xml配置文件,配置创建的对象
<bean id="userDao" class="cn.lych4.dao.impl.UserDaoImpl"></bean>
第二步: 有service 类 和 dao 类, 创建工厂类。进一步降低耦合度
class UserFactory{public static UserDao getDao() {String classValue = class属性值; // 1. XML解析Class clazz = Class.forName(classValue) // 通过反射创建对象return (UserDao)clazz.newInstance();}
}
IOC接口
IOC 思想基于IOC容器完成,IOC容器底层就算对象工厂
Spring 提供两种方式实现 IOC 容器: (两个接口)
- BeanFactory: IOC容器基本实现,是Spring 内部的使用接口,一般开发人员不使用
加载配置文件的时候,不会创建对象,在获取对象的时候才去创建对象 - ApplicationContext: 是BeanFactory的子接口,提供更多强大的功能,一般由开发人员使用。
加载配置文件的时候,就会把对象创建
IOC的bean管理
什么是Bean管理
Bean管理指的是两个操作:
- Spring 创建对象
- Spring 注入属性(set值)
Bean管理操作有两种方式
①. 基于XML配置文件方式实现, ②.基于注解方式实现
IOC的Bean管理操作(基于xml)
1.bean标签创建对象
在spring配置文件中,使用bean标签,标签里面添加对应属性,就可以是实现对象创建
*id属性: 对象标识
*class属性: 类全路径
name属性:早期属性,和id类似,只不过可以加特殊符号
默认执行无参构造完成对象创建
DI 依赖注入,注入属性
——set注入
对象类必须有对应set方法
配置文件:
<bean id="book" class="cn.lych4.spring5.bean.Book"><!-- set方法注入属性 --><property name="bName" value="易筋经"></property><property name="bAuthor" value="达摩老祖"></property></bean>
——有参构造注入
对象类必须有对应有参构造方法
配置文件:
<!-- 有参构造创建对象--><bean id="orders" class="cn.lych4.spring5.bean.Orders"><constructor-arg name="oname" value="电脑"></constructor-arg><constructor-arg name="address" value="China"></constructor-arg></bean>
xml注入其他类型属性,空值和bean值
1.字面量
null 空值
<!--注入空值--><property name="bAuthor"><null/></property>
属性值包括特殊符号
特殊符号转义,或者是把特殊内容写到 <![CDATA[这里]]
<!--注入带特殊符号的值, <[大神]> --><property name="bAuthor"><value><![CDATA[<[大神]>]]></value></property>
2.注入属性-外部bean
创建一个Service类和Dao类
在Service类中调用Dao类中的方法
在spring配置文件中进行配置
<!-- 1.service 和 dao 对象创建--><bean id="userService" class="cn.lych4.spring5.service.UserService">
<!-- 2.service 中注入 userDao 对象, name:类里面的属性名称--><property name="userDao" ref="userDaoImpl"></property></bean><bean id="userDaoImpl" class="cn.lych4.spring5.dao.UserDaoImpl"></bean>
3.注入属性-内部bean
(1)一对多关系:部门和员工
(2)在实体类中表示一对多的关系
//部门类,一
public class Dept {private String name;public void setName(String name) {this.name = name;}
}//员工类,多
public class Emp {private String ename;private String gender;//员工属于某一个部分private Dept dept;public void setEname(String ename) {this.ename = ename;}public void setGender(String gender) {this.gender = gender;}public void setDept(Dept dept) {this.dept = dept;}
}
(3)在spring配置文件中进行配置
<!-- 注入内部bean--><bean id="emp" class="cn.lych4.spring5.bean.Emp"><property name="ename" value="lucy"></property><property name="gender" value="女"></property>
<!-- 设置对象类型属性--><property name="dept"><bean id="dept" class="cn.lych4.spring5.bean.Dept"><property name="name" value="安保部"></property></bean></property></bean>
3.注入属性-级联赋值
(1) 第一种
<!-- 注入内部bean--><bean id="emp" class="cn.lych4.spring5.bean.Emp"><property name="ename" value="lucy"></property><property name="gender" value="女"></property>
<!-- 级联赋值--><property name="dept" ref="dept"></property></bean><bean id="dept" class="cn.lych4.spring5.bean.Dept"><property name="dname" value="财务部"></property></bean>
(2)第二种
<!-- 注入内部bean--><bean id="emp" class="cn.lych4.spring5.bean.Emp"><property name="ename" value="lucy"></property><property name="gender" value="女"></property>
<!-- 级联赋值--><property name="dept" ref="dept"></property><!-- 这里Emp类必须有dept属性的get方法--><property name="dept.dname" value="技术部"></property></bean><bean id="dept" class="cn.lych4.spring5.bean.Dept"><property name="dname" value="财务部"></property></bean>
xml注入集合属性
注入数组、List、Map
<bean id="stu" class="cn.lych4.spring5.collectiontype.Stu">
<!-- 数组类型注入--><property name="courses"><array><value>java课程</value><value>数据库课程</value></array></property>
<!-- list类型注入--><property name="list"><list><value>张三</value><value>三哥</value></list></property>
<!-- map类型属性注入--><property name="maps"><map><entry key="爱好" value="学习"></entry></map></property>
<!-- set类型属性注入--><property name="sets"><set><value>Java</value><value>MySQL</value></set></property></bean>
设置对象集合
<bean id="stu" class="cn.lych4.spring5.collectiontype.Stu">
<!-- list类型,里面是对象,属性注入--><property name="courseList"><list><ref bean="course1"></ref><ref bean="course2"></ref><ref bean="course3"></ref></list></property></bean><!-- 创建多个course对象--><bean id="course1" class="cn.lych4.spring5.collectiontype.Course" ><property name="cname" value="Java"></property></bean><bean id="course2" class="cn.lych4.spring5.collectiontype.Course" ><property name="cname" value="Python"></property></bean><bean id="course3" class="cn.lych4.spring5.collectiontype.Course" ><property name="cname" value="Go"></property></bean>
抽取集合值, 引入util命名空间
<?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:util="http://www.springframework.org/schema/util"xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd"><util:list id="bookList" ><value>MySQL必知必会</value><value>JVM调优</value></util:list><bean id="book" class="cn.lych4.spring5.collectiontype.Book" ><property name="bnames" ref="bookList"></property></bean>
</beans>
2. FactoryBean
Spring有两种类型的bean,一种是普通的bean 另外一种是工厂bean (FactoryBean)
- 普通bean: 在配置文件种定义bean类型就是返回类型
- 工厂bean:在配置文件定义bean类型可以和返回类型不一样
第一步:创建类,让这个类作为工厂bean, 实现接口FactoryBean
第二步:实现接口里面的方法,在实现的方法中定义返回的bean类型
创建一个Bean,实现FactoryBean接口
public class MyBean implements FactoryBean<Course> {//定义返回beanpublic Course getObject() throws Exception {Course course = new Course();course.setCname("abc");return course;}public Class<?> getObjectType() {return null;}public boolean isSingleton() {return false;}
}
xml配置文件
<bean id="myBean" class="cn.lych4.spring5.factorybean.MyBean"></bean>
测试:
@Testpublic void testFactoryBean() {ApplicationContext context = new ClassPathXmlApplicationContext("factorybean.xml");
// MyBean myBean = context.getBean("myBean", MyBean.class);Course myBean = context.getBean("myBean", Course.class);System.out.println(myBean);}
3. bean作用域
在Spring里面可以设置创建的bean实例是单实例还是多实例
在Spring里面默认情况下创建的bean都是单实例bean
scope属性可以设置单实例还是多实例。scope属性值: singleton、 prototype
<bean id="book" class="cn.lych4.spring5.collectiontype.Book" scope="prototype"></bean>
scope值是 singleton 的时候,加载spring配置文件就会创建实例对象
scope值是 prototype 时候, 不是在加载 spring 配置文件时候创建对象,获取对象的时候才会创建
4. bean生命周期
①通过构造器创建bean实例(无参构造)
②为bean的属性设置值和对其他bean引用(调用set方法)
③把bean实例传递bean后置处理器的Before方法(需要配置)
③调用bean的初始化的方法(需要进行配置初始化的方法)
③把bean实例传递bean后置处理器的After方法 (需要配置)
④bean可以使用了(对象获取到了)
⑤当容器关闭时候,调用bean的销毁的方法(需要进行配置销毁的方法)
<!-- 配置后置处理器, 所有bean都会加上后置处理器--><bean id="myBeanPost" class="cn.lych4.spring5.bean.MyBeanPost"></bean>
5. xml自动装配
bean标签属性autowire,配置自动装配。
两个属性常用值:
- byName 按属性名称注入,注入值bean的id值和类属性名得一样
- byType 按类型属性类型注入
6.xml引入外部属性文件
① 先直接配置数据库信息, 配置德鲁伊连接池
记得引入jar包
<!-- 配置连接池--><bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"><property name="driverClassName" value="com.mysql.jdbc.Driver"></property><property name="url" value="jdbc:mysql://localhost:3306/userDb"></property><property name="username" value="root"></property><property name="password" value="root"></property></bean>
② 引入外部属性文件
创建外部属性文件
引入context名称空间,把properties属性文件引入到 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"xmlns:context="http://www.springframework.org/schema/context"xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<!-- 引入外部属性文件--><context:property-placeholder location="jdbc.properties"/><bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"><property name="driverClassName" value="${prop.driverClass}"/><property name="url" value="${prop.url}"/><property name="password" value="${prop.password}"/></bean>
</beans>
IOC的Bean管理操作(基于注解)
1.基于注解实现对象创建
注解可以用在类、方法、属性上面
@Component
@Service
@Controller
@Repository
这四个注解功能一样,都可以用来创建bean实例
1.需要额外引入AOP的jar包
2.开启组件扫描
<!-- 开启组件扫描, 多个包逗号隔开, 或者直接扫描上层包 (引入名称空间context)--><context:component-scan base-package="cn.lych4.spring5"/>
3.创建类,在类上添加创建对象注解
@Component(value = "userService") //效果和 <bean id="userService" class="..."/> 一样
public class UserService {public void add() {System.out.println("service::add");}
}
常用包扫描设置:
<!-- 示例1: 不使用默认filter, 自己配置filter--><context:component-scan base-package="cn.lych4.spring5" use-default-filters="false">
<!-- 只扫描带Controller注解的类--><context:include-filter type="annotation"expression="org.springframework.stereotype.Controller"/></context:component-scan><!-- 示例2: 使用默认filter, 并且设置不扫描的注解--><context:component-scan base-package="cn.lych4.spring5">
<!-- 不扫描Controller注解的类--><context:exclude-filter type="annotation"expression="org.springframework.stereotype.Controller"/></context:component-scan>
2.基于注解实现属性注入
@Autowired : 根据属性类型进行自动装配
@Qualifier: 根据属性名称进行注入
@Resource: 可以根据类型注入,也可以根据名称注入
@Value : 注入普通类型属性
- Service类 和Dao类都加上创建对象注解(对象创建了)
- Service类需要注入Dao类属性, 先在Service类中定义Dao类型属性,不需要加set方法
- 在属性上面添加@Autowired 注解即可
Qualifier 注解要和Autowired 一起使用, 因为按类型注入一个接口可能有多个实现类,对应多个对象,按类型的话就找不到。
@Autowired@Qualifier(value = "userDao1")private UserDao userDao;
Resource 注解 既可以根据类型直接注入,也可以加个name来指定按名称找
Value 注解
@Value(value = "abc")private String name;
完全注解开发
包扫描配置都不要了
- 创建配置类,替代xml文件
- 编写测试类
@Testpublic void testService2() {ApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig.class);UserService userService = context.getBean("userService", UserService.class);userService.add();}
AOP
什么是AOP?
面向切面编程(AOP), 利用AOP可以对业务进行隔离,降低业务各部分之间的耦合度,提高代码重用性。提高开发效率
通过不修改源码的方式,在主干功能里面添加新的功能
AOP 底层原理
AOP底层使用动态代理
两种情况 的动态代理
①有接口的情况, 使用JDK的动态代理
一个接口,一个实现类
JDK动态代理, 创建一个UserDao 接口的实现类代理对象,去增强类的方法
②没有接口的情况,使用CGLIB动态代理
创建当前类的子类的代理对象
JDK动态代理
使用JDK动态代理,使用Proxy类里面的方法创建代理对象
三个参数分别是 类加载器、这个类实现的接口、实现InvocationHandler接口,写增强部分
代码:
public class JDKProxy {public static void main(String[] args) {//创建接口实现类代理对象Class[] interfaces = {UserDao.class};UserDaoImpl userDao = new UserDaoImpl();UserDao proxyUserDao = (UserDao) Proxy.newProxyInstance(JDKProxy.class.getClassLoader(), interfaces, new UserDaoProxy(userDao));int result = proxyUserDao.add(1, 2);System.out.println(result);}
}class UserDaoProxy implements InvocationHandler {private Object obj;//1. 创建的是谁的代理对象,把这个人传进来public UserDaoProxy(Object obj) {this.obj = obj;}//增强逻辑public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {//方法之前, 这里还可以根据方法名实现只增强接口中的某些方法System.out.println("方法执行之前...方法名:" + method.getName() + "...传递的参数:" + Arrays.toString(args)) ;//执行原方法Object ret = method.invoke(obj, args); //方法之后System.out.println("方法之后执行..." + obj);return ret;}
}
UserDao
public interface UserDao {public int add(int a, int b);
}
UserDaoImpl
public class UserDaoImpl implements UserDao {public int add(int a, int b) {System.out.println("add方法执行了");return a + b;}
}
AOP术语
1. 连接点: 类中哪些方法可以被增强,这些方法就称为连接点
2. 切入点: 实际被真正增强的方法叫做切除点
3. 通知(增强):实际增强的逻辑部分称为通知。有前置通知、后置通知、环绕通知、异常通知、最终通知
4. 切面 :是动作, 把通知应用到切入点的过程就是切面
实现AOP操作准备操作
Spring 框架中一般基于AspectJ实现AOP操作。
AspectJ是独立的AOP框架, 一般把AspectJ和Spring框架一起使用,进行AOP操作
基于AspectJ 实现AOP操作,两种方式: xml配置方式和注解方式。
1.引入jar包
2.切入点表达式
切入点表达式作用: 知道对哪个类里面的哪个方法进行增强
语法结构:
//举例1: 对cn.lych4.spring5.dao.BookDao 类里面的add()进行增强
execution(* cn.lych4.spring5.dao.BookDao.add(..))//举例2: 对cn.lych4.spring5.dao.BookDao 类里面所有方法进行增强
execution(* cn.lych4.spring5.dao.BookDao.*(..))//举例3: 对cn.lych4.spring5.dao 包里所有类里面所有方法进行增强
execution(* cn.lych4.spring5.dao.*.*(..))
实现AOP操作(AspectJ 注解)
1.创建类,在类中定义方法
@Component
public class User {public void add() {System.out.println("add........");}
}
2.创建增强类,编写增强逻辑,不同方法代表不同通知
@Component
@Aspect
public class UserProxy {//前置通知@Before(value = "execution(* cn.lych4.spring5.aopanno.User.add(..))")public void before() {System.out.println("before.....");}// 最终通知, 不管有没有异常都通知@After(value = "execution(* cn.lych4.spring5.aopanno.User.add(..))")public void after() {System.out.println("after.....");}//返回通知, 返回值执行完才通知 //后置通知@AfterReturning(value = "execution(* cn.lych4.spring5.aopanno.User.add(..))")public void afterReturning() {System.out.println("afterReturning.....");}//异常通知@AfterThrowing(value = "execution(* cn.lych4.spring5.aopanno.User.add(..))")public void afterThrowing() {System.out.println("afterThrowing.....");}//环绕通知, 方法前后都执行@Around(value = "execution(* cn.lych4.spring5.aopanno.User.add(..))")public void around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {System.out.println("around.....环绕之前....");proceedingJoinPoint.proceed();System.out.println("around.....环绕之后.....");}
}
3.进行通知配置
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"xmlns:context="http://www.springframework.org/schema/context"xmlns:aop="http://www.springframework.org/schema/aop"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
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="cn.lych4.spring5"/><!-- 开启Aspect生成代理对象--><aop:aspectj-autoproxy/>
</beans>
4.测试:
@Testpublic void testAopAnno(){ApplicationContext context = new ClassPathXmlApplicationContext("bean1.xml");User user = context.getBean("user", User.class);user.add();}
5.可以进行公共切入点抽取
//相同切入点抽取@Pointcut(value = "execution(* cn.lych4.spring5.aopanno.User.add(..))")public void pointDemo() {}//前置通知@Before(value = "pointDemo()")public void before() {System.out.println("before.....");}
6.如果有多个增强类对同一方法进行增强,可以设置增强优先级
给增强类添加一个@Order() 注解, 括号里填入数字,数字越小优先级越高
@Component
@Aspect
@Order(3)
public class PersonProxy {//前置通知@Before(value = "execution(* cn.lych4.spring5.aopanno.User.add(..))")public void before() {System.out.println("PersonProxy before.....");}
}
基于配置文件方式实现AOP操作(了解)
这样配置就欧克了
【2022年Spring全家桶】Spring5.x 框架详解相关推荐
- 【2022年Spring全家桶】学习路线图和资料整理
前言 对于Java工程师来说,几乎没有没听过大名鼎鼎的Spring框架的,Spring框架早已成为了Java后端开发事实上的行业标准,可以说,是Spring成就了Java,Spring也成为Java程 ...
- Spring全家桶中的日志框架
Spring全家桶中的日志框架 spring-jcl spring-jcl是spring的日志框架,spring-jcl底层使用的日志框架是有优先级的优先级为:LOG4J2 级是最高的,其次是SLF4 ...
- osgi框架和spring区别_最全153道Spring全家桶面试题,你都知道哪些?(含答案解析)...
前言 Spring 框架自诞生以来一直备受开发者青睐,有人亲切的称之为:Spring 全家桶. 毋庸置疑,Spring 早已成为 Java 后端开发的行业标准,无数的公司选择 Spring 作为基础的 ...
- 2022 年精选架构合集:Redis+Nginx+ 设计模式 +Spring 全家桶 +Dubbo
最近花了很长的时间去搜罗 Java 核心技术好文,我把每个 Java 核心技术的优选文章都整理成了一个又一个的文档.昨天也是终于全部整理好了,今天就把这些东西分享给老铁们,也能为老铁们省去不少麻烦,想 ...
- 2022年精选架构合集:Redis+Nginx+设计模式+Spring全家桶+Dubbo
最近花了很长的时间去搜罗Java核心技术好文,我把每个Java核心技术的优选文章都整理成了一个又一个的文档.昨天也是终于全部整理好了,今天就把这些东西分享给老铁们,也能为老铁们省去不少麻烦,想学什么技 ...
- 阿里技术2022年最新学习思路:高性能+微服务+分布式+spring全家桶
1.阿里巴巴的数据库连接池druid 2.阿里巴巴Dubbo+Zookeeper注册中心 3.阿里巴巴MyCat分库分表 4.pushlets推送技术 5.Linux操作系统下shell编程 6.Re ...
- Redis+Nginx+设计模式+Spring全家桶+Dubbo阿里P8技术精选文档
最近花了很长的时间去搜罗Java核心技术好文,我把每个Java核心技术的优选文章都整理成了一个又一个的文档.昨天也是终于全部整理好了,今天就把这些东西分享给老铁们,也能为老铁们省去不少麻烦,想学什么技 ...
- 网友提问:精通 Spring 全家桶,被 15 家公司拒绝,大专程序员出路在哪?
作者 l 会点代码的大叔(CodeDaShu) 最近一个网友向我发来私信说,他精通 Spring 全家桶,但是面试过程中被 15 家公司拒绝,因为他的学历是大专,所以担心是不是学历的原因被拒呢? 我给 ...
- Java 晋升必会的 70 道 「Spring 全家桶」高频面试题
对于那些想面试高级 Java 岗位的同学来说,除了算法属于比较「天方夜谭」的题目外,剩下针对实际工作的题目就属于真正的本事了,热门技术的细节和难点成为了面试时主要考察的内容. 这里说「天方夜谭」并不是 ...
- Java大厂技术文档:Redis+Nginx+设计模式+Spring全家桶+Dubbo精选
最近花了很长的时间去搜罗整理Java核心技术好文,我把每个Java核心技术的优选文章都整理成了一个又一个的文档.今天就把这些东西分享给老铁们,也能为老铁们省去不少麻烦,想学什么技能了,遇到哪方面的问题 ...
最新文章
- 从oracle中读取图片,Pb从oracle中读取和保存图片
- python基础语法3_python基础语法三
- Spring Boot Vue从零开始搭建系统(三):项目前后端分离
- Git Push 不用再次输入用户名和密码方法
- iOS 中导航控制器全屏向右滑动返回上一界面
- LinkedList方法源码
- python大于小于_在Python中大于/小于Pandas DataFrames / Series之间的比较
- qt中关于按钮的click()函数卓见
- python+ BeautifulSoup抓取“全国行政区划信息查询平台”的省市区信息
- 风控建模系列(六):催收评分卡卡跟贷前申请卡关于目标变量的定义
- Java技术——你真的了解String类的intern()方法吗
- 年前的面试经历(二)
- CS231n李飞飞计算机视觉 迁移学习之物体定位与检测下
- netbsd apache php mysql,NetBSD配置aria2的web前端YAAW笔记
- java-Effectively final
- 2009年第一天上班,祝大家工作顺利!
- 常用转义字符例如amp的含义
- STM32通过(软/硬IIC)控制LCD1602液晶显示屏(IIC转8位并口的PCF8574转接板的使用)原创
- 【android编程】第五讲-Android高级组件
- “大众点评开发者平台”正式上线