java进阶二
Java进阶第二部分
多线程
一个简单的线程
通过Thread.currentThread().getName();获取当前运行的线程
public class firstThread {
public static void main(String[] args) throws InterruptedException {
String string = "好好学习,天天向上,你是最棒的\n";
System.out.println(Thread.currentThread().getName());
printSlowly(string,400);
}
public static void printSlowly(String string,long interval) throws InterruptedException {
for(char ch:string.toCharArray()){
Thread.sleep(interval);
System.out.print(ch);
}
}
}
创建线程
用于共享命令行资源,因此此多线程Terminal输出会杂乱
thread.setDaemon(true);设置守护线程
thread.setPrority(Thread.MAX_PRORITY);设置线程优先级
TimeUnit.SECONDS.toMillis(xx)将时间按单位转换
public class secondThread {
private static final String string = "好好学习,天天向上,你是最棒的\n";
public static void main(String[] args) {
for (int i = 1; i <= 2; i++) {
Thread thread = new Thread(new PrintSome(string, 200 * i), "我的线程" + i);
// 设置守护线程,注意。是在需要截取其他线程的当前线程设置
thread.setDaemon(true);
// 设置优先级
// thread.setPriority(Thread.MAX_PRIORITY);
thread.start();
}
// 时间单位转换器
Thread.sleep(TimeUnit.SECONDS.toMillis(3));
System.out.println("end thread:" + Thread.currentThread().getName());
}
}
class PrintSome implements Runnable {
private final String text;
private final long interval;
public PrintSome(String text, long interval) {
this.text = text;
this.interval = interval;
}
@Override
public void run() {
try {
System.out.println("thread name:" + Thread.currentThread().getName());
firstThread.printSlowly(text, interval);
System.out.println("end thread:" + Thread.currentThread().getName());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
线程的InterruptedException
在当前线程sleep,wait和join时,如果被其创建的线程中断(或主动执行Interrupted方法)会抛出此异常,可以在catch此异常并进行相关操作
synchronized
同步锁,类似于python中的lock,js中的sync。不详细解释
wait notify
注意:sleep表示当前线程被阻塞,并不会释放资源。当被阻塞的线程被调用时,会抛出interrupted异常;当阻塞的线程sleep时间满后,如果其资源被其他线程锁定,就并不会立即执行,除非其具有更高的优先级
wait表示线程挂起,会释放资源让其他线程进入,使用notify会唤醒被挂起的线程,其使用必须在sync代码块中(因为必须是同步的线程才允许挂起,异步的资源无法准确释放),且并不会主动唤醒,会一直处于挂起状态
wait类似于一个标识,其他线程可以标识某个线程挂起,而阻塞只能由当前线程标识为阻塞状态
public class forthThread {
public static void main(String[] args) {
Object lock = new Object();
int workSec = 2;
int threadCount = 5;
System.out.println("--------------------invoke thread start sleep-----------------");
for (int i = 0; i < threadCount; i++) {
new Thread(() -> {
System.out.println("thread working:" + getThreadName());
// // 当前线程被阻塞,但持有的资源并不释放,注意,这里被阻塞2s(模拟工作),main线程继续执行
sleepSec(workSec);
synchronized (lock) {
// 利用阻塞模拟线程锁定并正在运行的状态,这里表示五个线程均在运行2s
System.out.println("thread begin to wait:" + getThreadName());
try {
// wait表示线程挂起,相关资源释放,其他线程可以访问,这里表示其他四个线程可以进入
lock.wait();
System.out.println("thread go on:" + getThreadName());
// 模拟被唤醒的线程又运行了2s
sleepSec(1);
System.out.println("thread end:" + getThreadName());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, "thread" + i).start();
}
sleepSec(workSec + 1);
System.out.println("--------------------invoke thread end sleep-----------------" + getThreadName());
synchronized (lock) {
System.out.println("--------------------invoke all thread-----------------");
lock.notifyAll();
}
}
private static void sleepSec(int sec) {
try {
Thread.sleep(TimeUnit.SECONDS.toMillis(sec));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
private static String getThreadName() {
return Thread.currentThread().getName();
}
}
join
join会判断所需join的线程是否为alive,如果在对应线程start或end后添加join,则该线程会立即返回,join的作用是将所需join的线程插入到当前运行的线程之前
for (Thread thread : threads) {
try {
String name = thread.getName();
System.out.println(" --- 主线程开始join " + name + " --- ");
// 这里仅仅是插入到当前线程,threads队列之间的线程还是独立的
thread.join();
System.out.println(" ---主线程join " + name + " 结束 --- ");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
解耦的思路:
1.通过反射来创建对象,避免使用new关键字
2.通过读取配置文件来获取要创建的对象的限定类名
Spring
一个小例子
使用jdbc与com.mysql依赖进行数据库读取
实际开发中应该左到:编译器不依赖,运行时才依赖
public class day1 {
public static void main(String[] args) throws SQLException {
// 1.注册驱动 2.获取连接 3.获取预处理对象 4.执行sql 5.遍历结果集 6.释放资源
DriverManager.registerDriver(new com.mysql.cj.jdbc.Driver());
// 通过反射调用,实现在运行时依赖
Class.forName("com.mysql.cj.jdbc.Driver");
Connection conn = DriverManager.getConnection("jdbc:mysql://192.168.43.217:3306/demo1?serverTimezone=Asia/Shanghai", "root", "123456");
PreparedStatement preparedStatement = conn.prepareStatement("select * from customer");
ResultSet set = preparedStatement.executeQuery();
while (set.next()){
System.out.println(set.getString("CUSTOMER_EMAIL"));
}
set.close();
preparedStatement.close();
conn.close();
}
}
maven
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.19</version>
<!--设置为运行时依赖 -->
<scope>runtime</scope>
</dependency>
Bean
ApplicationContext与BeanFactory
- 模拟一个表现层,用于调用业务层
- 获取spring 容器的IOC,并根据id获取对象
- ApplicationContext 常用的三个实现类:
-
ClassPathXmlApplicationContext:加载类路径下的config,配置文件必须在类路径下 -
FileSystemXmlApplicationContext:加载磁盘任一路径下的配置文件 -
AnnotationConfigApplicationContext:读取注解创建容器
-
- 核心容器的两个接口
- ApplicationContext: 单例对象使用(多),构建容器时,采用立即加载的方式(只要读取配置文件就创建)
- BeanFactory:多例对象使用,创建对象采用延时加载,即创建对象时加载
import com.learn2.dao.IAccountDao;
import com.learn2.service.IAccountSer;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.xml.XmlBeanFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
public class Client {
public static void main(String[] args) {
// 1.获取核心对象(ApplicationContext和BeanFactory两种方式)
useApplicationContext();
// ApplicationContext ac = useApplicationContext();
BeanFactory ac = useBeanFactory();
// 2.根据bean id获取对象(两种方式)
IAccountSer as = (IAccountSer)ac.getBean("accountService");
IAccountDao adao = ac.getBean("accountDao",IAccountDao.class);
System.out.println(as);
System.out.println(adao);
}
public static ApplicationContext useApplicationContext(){
// IAccountSer as = new AccountServiceImpl();
return new ClassPathXmlApplicationContext("bean.xml");
}
public static BeanFactory useBeanFactory(){
Resource resource = new ClassPathResource("bean.xml");
return new XmlBeanFactory(resource);
}
}
Bean对象的使用
-
创建Bean的三种方式
-
默认构造函数创建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"> <bean id="accountService" class="com.learn2.service.impl.AccountServiceImpl"/> <bean id="accountDao" class="com.learn2.dao.impl.AccountDaoImpl"/> </beans> -
使用工厂设计模式创建对象
<bean id="instanceFactory" class="com.learn2A.factory.InstanceFactory"/> <bean id="accountService_2A" factory-bean="instanceFactory" factory-method="getAccountService"/> -
类的静态方式,将上面两句结合起来就好(静态方法是属于类的,不是对象的,所以可以合在一起,但对应的工厂方法必须为静态的)
-
-
Bean的作用范围
- bean默认为单例模式 scope:singleton
- prototype:多例的
- request作用于web应用的请求范围,session->web应用的会话范围,global-session集群的全局会话范围
-
Bean对象的生命周期
注意:scope为singleton时模式为默认的ApplicationContext,即在加载配置时就创建bean,而模式为prototype时为BeanFactory模式,即在获取对应对象时才创建
-
单例对象:生命周期随容器相同
-
多例对象:生命周期随对象相同,但回收时由GC回收
<bean id="accountSer" class="com.learn2A.service.impl.AccountServiceImpl" init-method="init" destroy-method="destroy" scope="prototype"/>
-
public class Client {
public static void main(String[] args) {
ApplicationContext applicationContext = useBeanMethod();
IAccountSer iAccountSer = applicationContext.getBean("accountSer", AccountServiceImpl.class);
iAccountSer.saveAccount();
// 注意,父类ApplicationContext没有close方法,向上转型无法使用,所以向下转型后调用close;
ClassPathXmlApplicationContext classPathXmlApplicationContext = (ClassPathXmlApplicationContext)applicationContext;
classPathXmlApplicationContext.close();
}
public static ApplicationContext useBeanMethod(){
return new ClassPathXmlApplicationContext("bean.xml");
}
}
依赖注入
如果是经常变化的数据,并不适用于注入的方式
构造函数注入
注意:必须存在对应构造函数才能注入
<!-- name:args ; type:java.lang.String ; value:value ; index:args.indexOf ref:other bean plugin -->
<bean id="accountSer" class="com.learn2A.service.impl.AccountServiceImpl" init-method="init"
destroy-method="destroy">
<constructor-arg name="name" value="你猜我是谁"/>
<constructor-arg name="age" value="18"/>
<constructor-arg name="birthday" ref="now"/>
</bean>
<bean id="now" class="java.util.Date"/>
SetGet方法注入
<property name="name" value="Test"/>
<property name="age" value="1"/>
<property name="birthday" ref="now"/>
复杂类型注入
<property name="myList">
<list>
<value>AAA</value> <value>bbb</value> <value>ccc</value>
</list>
</property>
<property name="myStrs">
<array>
<value>null</value> <value>0000</value>
</array>
</property>
<property name="myMap">
<map>
<entry key="testA" value="testAA"/> <entry key="testB" value="testBB"/>
</map>
</property>
<property name="myProps">
<props>
<prop key="123">123</prop> <prop key="2233">2233</prop>
</props>
</property>
注解创建
若要读取到注解,需要在xml文件里写入
<!--告知spring创建容器时要扫描的包,在context空间约束中使用-->
<context:component-scan base-package="learnA.service"/>
@Component当前类对象存入容器,不写value时,默认类名为小写@Controller一般用于表现层Service一般用于业务层@Repository一般用于持久层
注入类型
-
@Autowired自动按照类型注入,只要容器中由唯一的一个bean对象类型和注入的变量类型匹配,就能成功,可以为变量,也可以为方法如果IOC容器中有多个类型匹配时(先按照类型匹配,类型一致时按照变量名与bean名寻找)
-
@Qualifier按照类注入的基础上再按照名称注入,必须与Autowired配合使用,用于处理多个类型匹配的问题,value值为类bean的名称 -
@Resource直接按照类的id注入 name:用于指定类的id// IAccountDao为接口,存在多个实现类时一下方法注入 // @Autowired // @Qualifier(value = "accountDao1") @Resource(name = "accountDao1") private IAccountDao accountDao;
以上为注入类型,而基本数据与String无法注入,且集合也无法注入
注入数据
@Value用于注入基本类型和String类型的数据,value指定数据的值,可以使用EL表达式:${}@scope用于改变作用范围 value :singleton prototype@PreDestroy指定销毁方法@PostConstruct指定初始化方法
使用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="accountService" class="com.smallPro.service.impl.AccountServiceImpl">
<property name="accountDao" ref="accountDao"/>
</bean>
<!--创建持久层对象-->
<bean id="accountDao" class="com.smallPro.dao.impl.AccountDaoImpl">
<property name="runner" ref="runner"/>
</bean>
<!--创建连接池-->
<bean id="runner" class="org.apache.commons.dbutils.QueryRunner" scope="prototype">
<constructor-arg name="ds" ref="dataSource"/>
</bean>
<!--创建数据源-->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="com.mysql.cj.jdbc.Driver"/>
<property name="jdbcUrl" value="jdbc:mysql://192.168.43.217:3306/eesy?serverTimezone=UTC"/>
<property name="user" value="root"/>
<property name="password" value="123456"/>
</bean>
</beans>
// 业务层中别忘了 private QueryRunner runner;
public class AccountServiceTest {
public static IAccountService accountService = null;
@Test
public void findAll() {
List<Account> accounts = accountService.findAllAccount();
for (Account account : accounts) {
System.out.println(account);
}
}
@BeforeClass
public static void before() throws Exception {
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("bean.xml");
AccountServiceTest.accountService = applicationContext.getBean("accountService", IAccountService.class);
}
@Test
public void findId() {
Account account = accountService.findAccountById(1);
System.out.println(account);
}
@Test
public void insertValue() {
Account account = new Account();
account.setName("name1");
account.setMoney((float) 2233);
accountService.saveAccount(account);
findAll();
}
@Test
public void UpdateValue() {
Account account = new Account();
account.setMoney((float) 999);
account.setName("some");
account.setId(4);
accountService.updateAccount(account);
findAll();
}
@Test
public void deleteValue() {
accountService.deleteAccount(2);
findAll();
}
}
注解方式:略,记着bean.xml里面加上<context:component-scan base-package="com.smallPro"/>
自动配置
@Configuration指定当前类是一个配置类,注意:当AnnotationConfigApplicationContext(SpringConfiguration.class);中的class为配置类时,此注解可以不用加,会自动到指定目录寻找,多个配置类之间可以用空格隔开,扫描包也可以不用啦
@ComponentScan指定spring在创建容器时要扫描的包 用value指定
@Bean用于把当前方法的返回值存入容器当中 name:指定bean的id,默认值为当前方法的名称
@Configuration
@ComponentScan(value = "com.smallPro")
public class SpringConfiguration {
/**
* 创建一个runner对象
* @param dataSource 数据源
* @return runner对象
*/
@Bean(name = "runner")
public QueryRunner createRunner(DataSource dataSource){
return new QueryRunner(dataSource);
}
/**
* 创建数据源对象
* @return 数据源
*/
@Bean
public DataSource createDataSourceA(){
ComboPooledDataSource comboPooledDataSource = new ComboPooledDataSource();
try {
comboPooledDataSource.setDriverClass("com.mysql.cj.jdbc.Driver");
comboPooledDataSource.setJdbcUrl("jdbc:mysql://192.168.43.217:3306/eesy?serverTimezone=UTC");
comboPooledDataSource.setUser("root");
comboPooledDataSource.setPassword("123456");
return comboPooledDataSource;
} catch (PropertyVetoException e) {
throw new RuntimeException(e);
}
}
}
// to use
@BeforeClass
public static void before() throws Exception {
// ApplicationContext applicationContext = new ClassPathXmlApplicationContext("bean.xml");
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(SpringConfiguration.class);
AccountServiceTest.accountService = applicationContext.getBean("accountService", IAccountService.class);
}
// 默认为单例模式,scope设置为prototype时,变为多例
public class QueryRunnerTest {
@Test
public void testQueryRunner() {
ApplicationContext ac = new AnnotationConfigApplicationContext(SpringConfiguration.class);
QueryRunner queryRunner = ac.getBean("runner",QueryRunner.class);
QueryRunner queryRunner1 = ac.getBean("runner",QueryRunner.class);
assert queryRunner!=queryRunner1;
}
}
@import用于导入其他的配置类,value参数值为对应配置类的.class类,注意:这个一般用于在总配置文件上导入其他子配置文件,总配置文件还是建议注解Configuration与componentScan
@PropertySource("classpath:xxx.properties")指定properties文件的路径。为了打jar包之后也能找到,因此需要加上classpath,表示类路径下
jdbc.driver = com.mysql.cj.jdbc.Driver
jdbc.url = jdbc:mysql://192.168.43.217:3306/eesy?serverTimezone=UTC
jdbc.username = root
jdbc.password = 123456
@PropertySource("classpath:jdbcConfig.properties")
@Value("${jdbc.driver}")
private String driver;
@Value("${jdbc.url}")
private String url;
@Value("${jdbc.username}")
private String username;
@Value("${jdbc.password}")
private String password;
Junit单元测试与Spring整合
@Runwith告知spring的运行器
@ContextConfiguration locations指定xml文件的位置,加上classpath关键字,表示类路径下,classes:指定注解类所在位置
然后通过Autowired就可以自动注入了
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = SpringConfiguration.class)
public class AccountServiceTest {
@Autowired
public IAccountService accountService;

浙公网安备 33010602011771号